Compare commits
	
		
			312 Commits
		
	
	
		
			v0.11.0
			...
			revert-126
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 64ff463200 | ||
|   | 08ca7659e7 | ||
|   | d7edfb4cc6 | ||
|   | 4eca80a770 | ||
|   | 56a662841e | ||
|   | 8878ea4cf7 | ||
|   | eb32a798b8 | ||
|   | 0759ba6722 | ||
|   | b8cb41b1da | ||
|   | 971b92acbb | ||
|   | d643efa9bb | ||
|   | 08c0aeb2d5 | ||
|   | b0940eb33e | ||
|   | 6ec858b237 | ||
|   | 891412bdb9 | ||
|   | 1947802a35 | ||
|   | 3b06e32b40 | ||
|   | a2711c2c08 | ||
|   | 3e6c9fa318 | ||
|   | 2e49c5932a | ||
|   | abf750f894 | ||
|   | 137434af1b | ||
|   | ac4d21b252 | ||
|   | db362a0efc | ||
|   | 887631500b | ||
|   | 65043d0ff3 | ||
|   | 1bf7c06b1e | ||
|   | 7b218c7f02 | ||
|   | 3572877a2e | ||
|   | f2d44114fa | ||
|   | 0833674b91 | ||
|   | 947b54555a | ||
|   | 1a88c9a651 | ||
|   | 567bb6fa2a | ||
|   | 00d0b71080 | ||
|   | 580e10b024 | ||
|   | fbe9621387 | ||
|   | 13b5e7c00e | ||
|   | 09d59f00e7 | ||
|   | 772d24b173 | ||
|   | cbd001e280 | ||
|   | bff4e120a7 | ||
|   | 8a0018177a | ||
|   | c9c8998fa2 | ||
|   | b0e842863d | ||
|   | 5b0fa90e39 | ||
|   | ae6f6565fa | ||
|   | c874ae7afd | ||
|   | c27c0c5b8c | ||
|   | ed102492e5 | ||
|   | e077f9ee18 | ||
|   | e9f4db7719 | ||
|   | 34e07b938d | ||
|   | 230230aa94 | ||
|   | 901846e6c1 | ||
|   | a93f41f1c3 | ||
|   | 7439d949c4 | ||
|   | e1276e464d | ||
|   | d248dfe346 | ||
|   | 91b63f78eb | ||
|   | 754d7b3cd7 | ||
|   | 7d950d067c | ||
|   | a34e89d56a | ||
|   | 4b51e604a7 | ||
|   | e3e7bd27e9 | ||
|   | f6ed0e8ab6 | ||
|   | c179d9e759 | ||
|   | 98f387b605 | ||
|   | 275dda820b | ||
|   | 43c32c2c7b | ||
|   | 3f5388c2e8 | ||
|   | bad24d6213 | ||
|   | 7a94efccbf | ||
|   | 3c72eea9ef | ||
|   | 3548cde9c4 | ||
|   | 3d17435438 | ||
|   | f185254114 | ||
|   | 943e2ebe14 | ||
|   | 0c27a5f361 | ||
|   | abfabc85b5 | ||
|   | e08704e6d3 | ||
|   | f60fc2ebad | ||
|   | b50278e92f | ||
|   | e0b6986851 | ||
|   | d913798d5b | ||
|   | 56267095cb | ||
|   | f9ce27def3 | ||
|   | 127a7a62c1 | ||
|   | 4429e847e8 | ||
|   | 56e321f6d9 | ||
|   | e135ed26f0 | ||
|   | 48b25fe842 | ||
|   | 85e57286ae | ||
|   | 5ef6617062 | ||
|   | ae89dcd62d | ||
|   | f9597fe799 | ||
|   | 0ec27350e0 | ||
|   | 156ba832f2 | ||
|   | c4f079dde2 | ||
|   | 2a2e9ef99d | ||
|   | 1446b16e77 | ||
|   | f03003b366 | ||
|   | d3db26499c | ||
|   | d1f67fd8b9 | ||
|   | 31ee92b711 | ||
|   | 1ea7fa3084 | ||
|   | fe217384aa | ||
|   | 49d2d5500f | ||
|   | c1afc34cfc | ||
|   | d8dc3c8445 | ||
|   | ea1855fc26 | ||
|   | 0be84a4e51 | ||
|   | d87ed1414e | ||
|   | 4382745012 | ||
|   | f73238787d | ||
|   | 8c04596859 | ||
|   | 79b16ddda6 | ||
|   | d490693bad | ||
|   | 6c9bd0b510 | ||
|   | 2740b6f957 | ||
|   | 37cf56a2b3 | ||
|   | e73bb4ff4a | ||
|   | ea35395d7e | ||
|   | bad901a162 | ||
|   | bb34528a86 | ||
|   | 487ec7907c | ||
|   | 3093ab8067 | ||
|   | 0f1d685ae6 | ||
|   | 24cb72e5b5 | ||
|   | 4c6ca58c95 | ||
|   | d62989bc5d | ||
|   | 8e39da6726 | ||
|   | 98deac042e | ||
|   | b51ffcada6 | ||
|   | a6386b1612 | ||
|   | 72ecbb0abc | ||
|   | c6caae8647 | ||
|   | da23bba392 | ||
|   | 32e7f57a7c | ||
|   | f7dfccb096 | ||
|   | 990c653397 | ||
|   | 911ece7612 | ||
|   | 222b4421e5 | ||
|   | 4517bd1356 | ||
|   | 5984ae46fc | ||
|   | 5bde1de61b | ||
|   | 06d229ea99 | ||
|   | 58391de007 | ||
|   | ceb3e8e3dd | ||
|   | 35a331f3fd | ||
|   | 7d13ce0626 | ||
|   | a49eebf9f5 | ||
|   | 989e4e1b75 | ||
|   | 9bb647e275 | ||
|   | 3dee082141 | ||
|   | 7d52d348b2 | ||
|   | ded52b8d19 | ||
|   | ab4ce0caba | ||
|   | 90c13a3959 | ||
|   | 9847c40e34 | ||
|   | e751461194 | ||
|   | a566265a72 | ||
|   | fecf419f83 | ||
|   | 26c14d2bca | ||
|   | 92bfea1773 | ||
|   | 40ad9c47a8 | ||
|   | de85f79ab6 | ||
|   | 3736f84c12 | ||
|   | e7a7025961 | ||
|   | e38cf1e2e8 | ||
|   | 7b84febfde | ||
|   | 39dcb937d5 | ||
|   | 3e3d3ebcf9 | ||
|   | e370271093 | ||
|   | 55603b927f | ||
|   | e35666bcc6 | ||
|   | 3a19890be9 | ||
|   | 725f8202ed | ||
|   | ee917b0e1d | ||
|   | a8900f3d6d | ||
|   | ea8f429902 | ||
|   | 219301a85b | ||
|   | 06a453333a | ||
|   | 5e0975ddfa | ||
|   | 76527b310d | ||
|   | 73b4385578 | ||
|   | bb3af4eff7 | ||
|   | 68adea7b5a | ||
|   | 4d3afb8efc | ||
|   | 852ac3cd55 | ||
|   | 40abd1ce6c | ||
|   | 3a7e98ab84 | ||
|   | 6364bbc6b3 | ||
|   | fc7d2672db | ||
|   | 3a25704f41 | ||
|   | 98486f718b | ||
|   | a7f830177a | ||
|   | 2c2d9c1bfe | ||
|   | cc6b9d4099 | ||
|   | 1a284ac9dc | ||
|   | b48ca99119 | ||
|   | 2e8d021a6a | ||
|   | 5409e3d203 | ||
|   | f0064fd91f | ||
|   | c3d158584b | ||
|   | 6b8a2a7770 | ||
|   | cbb7ada63c | ||
|   | fe00d3e501 | ||
|   | b8eeeaeede | ||
|   | dd4e20cfb5 | ||
|   | 597d66782e | ||
|   | 01031de074 | ||
|   | 1cc29be3d6 | ||
|   | 6cbdaa442b | ||
|   | ab9d92a01a | ||
|   | 8aa3000fb1 | ||
|   | 64d8b5fcd1 | ||
|   | 05e88e5dcf | ||
|   | 5a819a96de | ||
|   | 7998a3fc98 | ||
|   | 9063996e84 | ||
|   | 76e62ae199 | ||
|   | 0e24de2b69 | ||
|   | 542dea69a1 | ||
|   | 89f2e0943c | ||
|   | 08eaa822fe | ||
|   | 39f2f94a53 | ||
|   | b02de171f1 | ||
|   | 4091e9cdf4 | ||
|   | b165322ba3 | ||
|   | 30750e63aa | ||
|   | 2cb636b050 | ||
|   | 5161205a06 | ||
|   | 79c5895f2f | ||
|   | 44f8c2f49f | ||
|   | 702ebbd557 | ||
|   | e29efbbcdf | ||
|   | 44504feeb9 | ||
|   | fdbe31cb76 | ||
|   | 10d2794bb7 | ||
|   | eb3b6e5eca | ||
|   | ccc32096ea | ||
|   | 4e5887d9ec | ||
|   | 2167c061a2 | ||
|   | 2100c5717e | ||
|   | acc1b8e207 | ||
|   | f3bd88837f | ||
|   | 8105a7edb8 | ||
|   | 385d90b056 | ||
|   | ecf526aa03 | ||
|   | a35b158fd1 | ||
|   | 2ebf05ae5b | ||
|   | 850da9de52 | ||
|   | 353c24c70e | ||
|   | 73d10ab093 | ||
|   | 668b7b6a39 | ||
|   | 70c072be0b | ||
|   | eb2fbcd8d0 | ||
|   | 02f34d1c04 | ||
|   | c8931f8535 | ||
|   | 0274490b68 | ||
|   | 06f2c74444 | ||
|   | 4d89de7068 | ||
|   | 2d50a63ca4 | ||
|   | 54dcccb472 | ||
|   | d1e1ba1c66 | ||
|   | 7265b241a2 | ||
|   | 59436f7b45 | ||
|   | 771542ee5f | ||
|   | b60eff2f5e | ||
|   | f6d6cb929f | ||
|   | 6f2c80bc2e | ||
|   | 0fe0b21eda | ||
|   | 0bedc227f4 | ||
|   | 6c4419fb72 | ||
|   | 766e94ed62 | ||
|   | 4d2f159dd8 | ||
|   | 41156cec7c | ||
|   | 9fa6184b0a | ||
|   | fb8fe93529 | ||
|   | 26839c7fd1 | ||
|   | 264896c264 | ||
|   | e3208187bf | ||
|   | bcf85db9c4 | ||
|   | f1e81c359e | ||
|   | 8279acc9a5 | ||
|   | 63438ad9ba | ||
|   | 467f4d2257 | ||
|   | a5aaa6f103 | ||
|   | 9b273bc148 | ||
|   | 0aba9122a1 | ||
|   | ac3706dfb6 | ||
|   | 3905171457 | ||
|   | 1e03f22086 | ||
|   | 360fdeaec8 | ||
|   | d5914dfde9 | ||
|   | d3f2a24267 | ||
|   | f85ac79b4a | ||
|   | bea9f22a0c | ||
|   | 3f330dccf1 | ||
|   | f7f934f0e8 | ||
|   | 0e683acde1 | ||
|   | dd6414daf0 | ||
|   | 8756b7213f | ||
|   | b0cbb5a2b4 | ||
|   | 6418b0c634 | ||
|   | 433d26e703 | ||
|   | c99ffcb1f4 | ||
|   | ef815cf060 | ||
|   | 7fb7a5452f | ||
|   | 4b641f8723 | ||
|   | 0d312def27 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ | ||||
| *.gzip | ||||
| *.tgz | ||||
| *.DS_Store | ||||
| *.swp | ||||
|  | ||||
| # Compiled CSS, unless directly added | ||||
| *.sass-cache | ||||
|   | ||||
							
								
								
									
										278
									
								
								API.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								API.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| # Open MCT API | ||||
|  | ||||
| The Open MCT framework public api can be utilized by building the application | ||||
| (`gulp install`) and then copying the file from `dist/main.js` to your | ||||
| directory of choice. | ||||
|  | ||||
| Open MCT supports AMD, CommonJS, and loading via a script tag; it's easy to use | ||||
| in your project. The [`openmct`]{@link module:openmct} module is exported | ||||
| via AMD and CommonJS, and is also exposed as `openmct` in the global scope | ||||
| if loaded via a script tag. | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| Open MCT's goal is to allow you to browse, create, edit, and visualize all of | ||||
| the domain knowledge you need on a daily basis. | ||||
|  | ||||
| To do this, the main building block provided by Open MCT is the _domain object_. | ||||
| The temperature sensor on the starboard solar panel, | ||||
| an overlay plot comparing the results of all temperature sensor, | ||||
| the command dictionary for a spacecraft, | ||||
| the individual commands in that dictionary, your "my documents" folder: | ||||
| All of these things are domain objects. | ||||
|  | ||||
| Domain objects have Types, so a specific instrument temperature sensor is a | ||||
| "Telemetry Point," and turning on a drill for a certain duration of time is | ||||
| an "Activity".  Types allow you to form an ontology of knowledge and provide | ||||
| an abstraction for grouping, visualizing, and interpreting data. | ||||
|  | ||||
| And then we have Views. Views allow you to visualize domain objects. Views can | ||||
| apply to specific domain objects; they may also apply to certain types of | ||||
| domain objects, or they may apply to everything.  Views are simply a method | ||||
| of visualizing domain objects. | ||||
|  | ||||
| Regions allow you to specify what views are displayed for specific types of | ||||
| domain objects in response to different user actions. For instance, you may | ||||
| want to display a different view while editing, or you may want to update the | ||||
| toolbar display when objects are selected.  Regions allow you to map views to | ||||
| specific user actions. | ||||
|  | ||||
| Domain objects can be mutated and persisted, developers can create custom | ||||
| actions and apply them to domain objects, and many more things can be done. | ||||
| For more information, read on! | ||||
|  | ||||
| ## Running Open MCT | ||||
|  | ||||
| Once the [`openmct`](@link module:openmct) module has been loaded, you can | ||||
| simply invoke [`start`]{@link module:openmct.MCT#start} to run Open MCT: | ||||
|  | ||||
|  | ||||
| ``` | ||||
| openmct.start(); | ||||
| ``` | ||||
|  | ||||
| Generally, however, you will want to configure Open MCT by adding plugins | ||||
| before starting it. It is important to install plugins and configure Open MCT | ||||
| _before_ calling [`start`]{@link module:openmct.MCT#start}; Open MCT is not | ||||
| designed to be reconfigured once started. | ||||
|  | ||||
| ## Configuring Open MCT | ||||
|  | ||||
| The [`openmct`]{@link module:openmct} module (more specifically, the | ||||
| [`MCT`]{@link module:openmct.MCT} class, of which `openmct` is an instance) | ||||
| exposes a variety of methods to allow the application to be configured, | ||||
| extended, and customized before running. | ||||
|  | ||||
| Short examples follow; see the linked documentation for further details. | ||||
|  | ||||
| ### Adding Domain Object Types | ||||
|  | ||||
| Custom types may be registered via | ||||
| [`openmct.types`]{@link module:openmct.MCT#types}: | ||||
|  | ||||
| ``` | ||||
| openmct.types.addType('my-type', new openmct.Type({ | ||||
|     label: "My Type", | ||||
|     description: "This is a type that I added!" | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ### Adding Views | ||||
|  | ||||
| Custom views may be registered based on the region in the application | ||||
| where they should appear: | ||||
|  | ||||
| * [`openmct.mainViews`]{@link module:openmct.MCT#mainViews} is a registry | ||||
|   of views of domain objects which should appear in the main viewing area. | ||||
| * [`openmct.inspectors`]{@link module:openmct.MCT#inspectors} is a registry | ||||
|   of views of domain objects and/or active selections, which should appear in | ||||
|   the Inspector. | ||||
| * [`openmct.toolbars`]{@link module:openmct.MCT#toolbars} is a registry | ||||
|   of views of domain objects and/or active selections, which should appear in | ||||
|   the toolbar area while editing. | ||||
| * [`openmct.indicators`]{@link module:openmct.MCT#inspectors} is a registry | ||||
|   of views which should appear in the status area of the application. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| ``` | ||||
| openmct.mainViews.addProvider({ | ||||
|     canView: function (domainObject) { | ||||
|         return domainObject.type === 'my-type'; | ||||
|     }, | ||||
|     view: function (domainObject) { | ||||
|         return new MyView(domainObject); | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ### Adding a Root-level Object | ||||
|  | ||||
| In many cases, you'd like a certain object (or a certain hierarchy of | ||||
| objects) to be accessible from the top level of the application (the | ||||
| tree on the left-hand side of Open MCT.) It is typical to expose a telemetry | ||||
| dictionary as a hierarchy of telemetry-providing domain objects in this | ||||
| fashion. | ||||
|  | ||||
| To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method | ||||
| of the [object API]{@link module:openmct.ObjectAPI}: | ||||
|  | ||||
| ``` | ||||
| openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" }); | ||||
| ``` | ||||
|  | ||||
| Root objects are loaded just like any other objects, i.e. via an object | ||||
| provider. | ||||
|  | ||||
| ### Adding Composition Providers | ||||
|  | ||||
| The "composition" of a domain object is the list of objects it contains, | ||||
| as shown (for example) in the tree for browsing. Open MCT provides a | ||||
| default solution for composition, but there may be cases where you want | ||||
| to provide the composition of a certain object (or type of object) dynamically. | ||||
| For instance, you may want to populate a hierarchy under a custom root-level | ||||
| object based on the contents of a telemetry dictionary. | ||||
| To do this, you can add a new CompositionProvider: | ||||
|  | ||||
| ``` | ||||
| openmct.composition.addProvider({ | ||||
|     appliesTo: function (domainObject) { | ||||
|         return domainObject.type === 'my-type'; | ||||
|     }, | ||||
|     load: function (domainObject) { | ||||
|         return Promise.resolve(myDomainObjects); | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ### Adding Telemetry Providers | ||||
|  | ||||
| When connecting to a new telemetry source, you will want to register a new | ||||
| [telemetry provider]{@link module:openmct.TelemetryAPI~TelemetryProvider} | ||||
| with the [telemetry API]{@link module:openmct.TelemetryAPI#addProvider}: | ||||
|  | ||||
| ``` | ||||
| openmct.telemetry.addProvider({ | ||||
|     canProvideTelemetry: function (domainObject) { | ||||
|         return domainObject.type === 'my-type'; | ||||
|     }, | ||||
|     properties: function (domainObject) { | ||||
|         return [ | ||||
|             { key: 'value', name: "Temperature", units: "degC" }, | ||||
|             { key: 'time', name: "UTC" } | ||||
|         ]; | ||||
|     }, | ||||
|     request: function (domainObject, options) { | ||||
|         var telemetryId = domainObject.myTelemetryId; | ||||
|         return myAdapter.request(telemetryId, options.start, options.end); | ||||
|     }, | ||||
|     subscribe: function (domainObject, callback) { | ||||
|         var telemetryId = domainObject.myTelemetryId; | ||||
|         myAdapter.subscribe(telemetryId, callback); | ||||
|         return myAdapter.unsubscribe.bind(myAdapter, telemetryId, callback); | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| The implementations for `request` and `subscribe` can vary depending on the | ||||
| nature of the endpoint which will provide telemetry. In the example above, | ||||
| it is assumed that `myAdapter` contains the specific implementations | ||||
| (HTTP requests, WebSocket connections, etc.) associated with some telemetry | ||||
| source. | ||||
|  | ||||
| ## Using Open MCT | ||||
|  | ||||
| When implementing new features, it is useful and sometimes necessary to | ||||
| utilize functionality exposed by Open MCT. | ||||
|  | ||||
| ### Retrieving Composition | ||||
|  | ||||
| To limit which objects are loaded at any given time, the composition of | ||||
| a domain object must be requested asynchronously: | ||||
|  | ||||
| ``` | ||||
| openmct.composition(myObject).load().then(function (childObjects) { | ||||
|     childObjects.forEach(doSomething); | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ### Support Common Gestures | ||||
|  | ||||
| Custom views may also want to support common gestures using the | ||||
| [gesture API]{@link module:openmct.GestureAPI}. For instance, to make | ||||
| a view (or part of a view) selectable: | ||||
|  | ||||
| ``` | ||||
| openmct.gestures.selectable(myHtmlElement, myDomainObject); | ||||
| ``` | ||||
|  | ||||
| ### Working with Domain Objects | ||||
|  | ||||
| The [object API]{@link module:openmct.ObjectAPI} provides useful methods | ||||
| for working with domain objects. | ||||
|  | ||||
| To make changes to a domain object, use the | ||||
| [`mutate`]{@link module:openmct.ObjectAPI#mutate} method: | ||||
|  | ||||
| ``` | ||||
| openmct.objects.mutate(myDomainObject, "name", "New name!"); | ||||
| ``` | ||||
|  | ||||
| Making modifications in this fashion allows other usages of the domain | ||||
| object to remain up to date using the | ||||
| [`observe`]{@link module:openmct.ObjectAPI#observe} method: | ||||
|  | ||||
| ``` | ||||
| openmct.objects.observe(myDomainObject, "name", function (newName) { | ||||
|     myLabel.textContent = newName; | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ### Using Telemetry | ||||
|  | ||||
| Very often in Open MCT, you wish to work with telemetry data (for instance, | ||||
| to display it in a custom visualization.) | ||||
|  | ||||
|  | ||||
| ### Synchronizing with the Time Conductor | ||||
|  | ||||
| Views which wish to remain synchronized with the state of Open MCT's | ||||
| time conductor should utilize | ||||
| [`openmct.conductor`]{@link module:openmct.TimeConductor}: | ||||
|  | ||||
| ``` | ||||
| openmct.conductor.on('bounds', function (newBounds) { | ||||
|     requestTelemetry(newBounds.start, newBounds.end).then(displayTelemetry); | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| ## Plugins | ||||
|  | ||||
| While you can register new features with Open MCT directly, it is generally | ||||
| more useful to package these as a plugin. A plugin is a function that takes | ||||
| [`openmct`]{@link module:openmct} as an argument, and performs configuration | ||||
| upon `openmct` when invoked. | ||||
|  | ||||
| ### Installing Plugins | ||||
|  | ||||
| To install plugins, use the [`install`]{@link module:openmct.MCT#install} | ||||
| method: | ||||
|  | ||||
| ``` | ||||
| openmct.install(myPlugin); | ||||
| ``` | ||||
|  | ||||
| The plugin will be invoked to configure Open MCT before it is started. | ||||
|  | ||||
| ### Writing Plugins | ||||
|  | ||||
| Plugins configure Open MCT, and should utilize the | ||||
| [`openmct`]{@link module:openmct} module to do so, as summarized above in | ||||
| "Configuring Open MCT" and "Using Open MCT" above. | ||||
|  | ||||
| ### Distributing Plugins | ||||
|  | ||||
| Hosting or downloading plugins is outside of the scope of this documentation. | ||||
| We recommend distributing plugins as UMD modules which export a single | ||||
| function. | ||||
|  | ||||
							
								
								
									
										129
									
								
								LICENSES.md
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								LICENSES.md
									
									
									
									
									
								
							| @@ -560,3 +560,132 @@ The above copyright notice and this permission notice shall be included in all c | ||||
|  | ||||
| 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. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Almond | ||||
|  | ||||
| * Link: https://github.com/requirejs/almond | ||||
|  | ||||
| * Version: 0.3.3 | ||||
|  | ||||
| * Author: jQuery Foundation | ||||
|  | ||||
| * Description: Lightweight RequireJS replacement for builds | ||||
|  | ||||
| #### License | ||||
|  | ||||
| Copyright jQuery Foundation and other contributors, https://jquery.org/ | ||||
|  | ||||
| This software consists of voluntary contributions made by many | ||||
| individuals. For exact contribution history, see the revision history | ||||
| available at https://github.com/requirejs/almond | ||||
|  | ||||
| The following license applies to all parts of this software except as | ||||
| documented below: | ||||
|  | ||||
| ==== | ||||
|  | ||||
| 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. | ||||
|  | ||||
| ==== | ||||
|  | ||||
| Copyright and related rights for sample code are waived via CC0. Sample | ||||
| code is defined as all source code displayed within the prose of the | ||||
| documentation. | ||||
|  | ||||
| CC0: http://creativecommons.org/publicdomain/zero/1.0/ | ||||
|  | ||||
| ==== | ||||
|  | ||||
| Files located in the node_modules directory, and certain utilities used | ||||
| to build or test the software in the test and dist directories, are | ||||
| externally maintained libraries used by this software which have their own | ||||
| licenses; we recommend you read them, as their terms may differ from the | ||||
| terms above. | ||||
|  | ||||
|  | ||||
| ### Lodash | ||||
|  | ||||
| * Link: https://lodash.com | ||||
|  | ||||
| * Version: 3.10.1 | ||||
|  | ||||
| * Author: Dojo Foundation | ||||
|  | ||||
| * Description: Utility functions | ||||
|  | ||||
| #### License | ||||
|  | ||||
| Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/> | ||||
| Based on Underscore.js, copyright 2009-2015 Jeremy Ashkenas, | ||||
| DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/> | ||||
|  | ||||
| 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. | ||||
|  | ||||
| ### EventEmitter3 | ||||
|  | ||||
| * Link: https://github.com/primus/eventemitter3 | ||||
|  | ||||
| * Version: 1.2.0 | ||||
|  | ||||
| * Author: Arnout Kazemier | ||||
|  | ||||
| * Description: Event-driven programming support | ||||
|  | ||||
| #### License | ||||
|  | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2014 Arnout Kazemier | ||||
|  | ||||
| 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. | ||||
|   | ||||
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							| @@ -9,9 +9,30 @@ Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting S | ||||
| Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/). | ||||
|  | ||||
|  | ||||
| ## New API | ||||
|  | ||||
| A simpler, [easier-to-use API](https://nasa.github.io/openmct/docs/api/) | ||||
| has been added to Open MCT. Changes in this | ||||
| API include a move away from a declarative system of JSON configuration files | ||||
| towards an imperative system based on function calls. Developers will be able | ||||
| to extend and build on Open MCT by making direct function calls to a public | ||||
| API. Open MCT is also being refactored to minimize the dependencies that using | ||||
| Open MCT imposes on developers, such as the current requirement to use | ||||
| AngularJS. | ||||
|  | ||||
| This new API has not yet been heavily used and is likely to contain defects. | ||||
| You can help by trying it out, and reporting any issues you encounter | ||||
| using our GitHub issue tracker. Such issues may include bugs, suggestions, | ||||
| missing documentation, or even just requests for help if you're having | ||||
| trouble. | ||||
|  | ||||
| We want Open MCT to be as easy to use, install, run, and develop for as | ||||
| possible, and your feedback will help us get there! | ||||
|  | ||||
| ## Building and Running Open MCT Locally | ||||
|  | ||||
| Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website. | ||||
| (These instructions assume you are installing as a non-root user; developers have [reported issues](https://github.com/nasa/openmct/issues/1151) running these steps with root privileges.) | ||||
|  | ||||
| 1. Clone the source code | ||||
|  | ||||
| @@ -31,6 +52,22 @@ Open MCT is now running, and can be accessed by pointing a web browser at [http: | ||||
|  | ||||
| Documentation is available on the [Open MCT website](https://nasa.github.io/openmct/documentation/). The documentation can also be built locally. | ||||
|  | ||||
| ### Examples | ||||
|  | ||||
| The clearest examples for developing Open MCT plugins are in the | ||||
| [tutorials](https://nasa.github.io/openmct/docs/tutorials/) provided in | ||||
| our documentation. | ||||
|  | ||||
| For a practical example of a telemetry adapter, see David Hudson's | ||||
| [Kerbal Space Program plugin](https://github.com/hudsonfoo/kerbal-openmct), | ||||
| which allows [Kerbal Space Program](https://kerbalspaceprogram.com) players | ||||
| to build and use displays for their own missions in Open MCT. | ||||
|  | ||||
| Additional examples are available in the `examples` hierarchy of this | ||||
| repository; however, be aware that these examples are | ||||
| [not fully-documented](https://github.com/nasa/openmct/issues/846), so | ||||
| the tutorials will likely serve as a better starting point. | ||||
|  | ||||
| ### Building the Open MCT Documentation Locally | ||||
| Open MCT's documentation is generated by an | ||||
| [npm](https://www.npmjs.com/)-based build.  It has additional dependencies that | ||||
|   | ||||
| @@ -13,11 +13,16 @@ | ||||
|     "moment-duration-format": "^1.3.0", | ||||
|     "requirejs": "~2.1.22", | ||||
|     "text": "requirejs-text#^2.0.14", | ||||
|     "es6-promise": "^3.0.2", | ||||
|     "es6-promise": "^3.3.0", | ||||
|     "screenfull": "^3.0.0", | ||||
|     "node-uuid": "^1.4.7", | ||||
|     "comma-separated-values": "^3.6.4", | ||||
|     "FileSaver.js": "^0.0.2", | ||||
|     "zepto": "^1.1.6" | ||||
|     "zepto": "^1.1.6", | ||||
|     "eventemitter3": "^1.2.0", | ||||
|     "lodash": "3.10.1", | ||||
|     "almond": "~0.3.2", | ||||
|     "d3": "~4.1.0", | ||||
|     "html2canvas": "^0.4.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|  | ||||
| # Script to build and deploy docs. | ||||
|  | ||||
| OUTPUT_DIRECTORY="target/docs" | ||||
| OUTPUT_DIRECTORY="dist/docs" | ||||
| # Docs, once built, are pushed to the private website repo | ||||
| REPOSITORY_URL="git@github.com:nasa/openmct-website.git" | ||||
| WEBSITE_DIRECTORY="website" | ||||
| @@ -32,7 +32,7 @@ WEBSITE_DIRECTORY="website" | ||||
| BUILD_SHA=`git rev-parse HEAD` | ||||
|  | ||||
| # A remote will be created for the git repository we are pushing to. | ||||
| # Don't worry, as this entire directory will get trashed inbetween builds. | ||||
| # Don't worry, as this entire directory will get trashed in between builds. | ||||
| REMOTE_NAME="documentation" | ||||
| WEBSITE_BRANCH="master" | ||||
|  | ||||
| @@ -45,8 +45,8 @@ npm run docs | ||||
|  | ||||
| echo "git clone $REPOSITORY_URL website" | ||||
| git clone $REPOSITORY_URL website || exit 1 | ||||
| echo "cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY/docs" | ||||
| cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY/docs | ||||
| echo "cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY" | ||||
| cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY | ||||
| echo "cd $WEBSITE_DIRECTORY" | ||||
| cd $WEBSITE_DIRECTORY || exit 1 | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ overall architecture of Open MCT. | ||||
| The target audience includes: | ||||
|  | ||||
| * _Platform maintainers_: Individuals involved in developing, | ||||
|   extending, and maintaing capabilities of the platform. | ||||
|   extending, and maintaining capabilities of the platform. | ||||
| * _Integration developers_: Individuals tasked with integrated | ||||
|   Open MCT into a larger system, who need to understand | ||||
|   its inner workings sufficiently to complete this integration. | ||||
| @@ -63,7 +63,7 @@ These layers are: | ||||
|   application-specific knowledge; at this layer, we have only | ||||
|   established an abstraction by which different software components | ||||
|   may communicate and/or interact. | ||||
| * [_Platform_](platform.md): The platform layer defines the general look,  | ||||
| * [_Platform_](platform.md): The platform layer defines the general look, | ||||
|   feel, and behavior of Open MCT. This includes user-facing components like | ||||
|   Browse mode and Edit mode, as well as underlying elements of the | ||||
|   information model and the general service infrastructure. | ||||
| @@ -74,5 +74,3 @@ These layers are: | ||||
|   typically consists of a mix of custom plug-ins to Open MCT, | ||||
|   as well as optional features (such as Plot view) included alongside | ||||
|   the platform. | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # Overview | ||||
|  | ||||
| The Open MCT platform utilizes the [framework layer](Framework.md) | ||||
| The Open MCT platform utilizes the [framework layer](framework.md) | ||||
| to provide an extensible baseline for applications which includes: | ||||
|  | ||||
| * A common user interface (and user interface paradigm) for dealing with | ||||
| @@ -38,7 +38,7 @@ in __any of these tiers__. | ||||
|   are initiated from here and invoke behavior in the presentation layer. HTML  | ||||
|   templates are written in Angular’s template syntax; see the [Angular documentation on templates](https://docs.angularjs.org/guide/templates).  | ||||
|   These describe the page as actually seen by the user. Conceptually,  | ||||
|   stylesheets (controlling the lookandfeel of the rendered templates) belong  | ||||
|   stylesheets (controlling the look-and-feel of the rendered templates) belong  | ||||
|   in this grouping as well.  | ||||
| * [_Presentation layer_](#presentation-layer): The presentation layer | ||||
|   is responsible for updating (and providing information to update) | ||||
| @@ -48,7 +48,7 @@ in __any of these tiers__. | ||||
|   display. | ||||
| * [_Information model_](#information-model): Provides a common (within Open MCT  | ||||
|   Web) set of interfaces for dealing with “things”  domain objects  within the  | ||||
|   system. Userfacing concerns in a Open MCT Web application are expressed as  | ||||
|   system. User-facing concerns in a Open MCT Web application are expressed as  | ||||
|   domain objects; examples include folders (used to organize other domain  | ||||
|   objects), layouts (used to build displays), or telemetry points (used as  | ||||
|   handles for streams of remote measurements.) These domain objects expose a  | ||||
| @@ -616,7 +616,7 @@ follows: | ||||
|   part of an action's extension definition. | ||||
| * `CreateActionProvider` provides the various Create actions which | ||||
|   populate the Create menu. These are driven by the available types, | ||||
|   so do not map easily ot extension category `actions`; instead, these | ||||
|   so do not map easily to extension category `actions`; instead, these | ||||
|   are generated after looking up which actions are available from the | ||||
|   [`TypeService`](#type-service). | ||||
| * `ActionAggregator` merges together actions from multiple providers. | ||||
|   | ||||
| @@ -98,7 +98,7 @@ Worked on bug fixes in the platform and a plugin for search. | ||||
|   It is hard to figure out what the difference between the various ways of | ||||
|   dealing with telemetry are. e.g., what is the difference between just | ||||
|   "Telemetry" and the "Telemetry Service"? There are many | ||||
|   "Telemetry Thing"s which seem related, but in an unclear way. | ||||
|   "Telemetry Things" which seem related, but in an unclear way. | ||||
|  | ||||
| ### Developer Intern 2 | ||||
|  | ||||
| @@ -180,7 +180,7 @@ to develop a tabular visualization plugin. | ||||
|       * Add a model property to the bundle.json to take in "Hello World" | ||||
|         as a parameter and pass through to the controller/view | ||||
|  | ||||
| ### Open Source Contributer | ||||
| ### Open Source Contributor | ||||
|  | ||||
|  * [Failures are non-graceful when services are missing.]( | ||||
|    https://github.com/nasa/openmctweb/issues/79) | ||||
| @@ -214,7 +214,7 @@ to an entirely different framework. | ||||
|  | ||||
| We can expect AngularJS 1.x to reach end-of-life reasonably soon thereafter. | ||||
|  | ||||
| Our API is currently a superset of Angular's API, so this directly effects | ||||
| Our API is currently a superset of Angular's API, so this directly affects | ||||
| our API. Specifically, API changes should be oriented towards removing | ||||
| or reducing the Angular dependency. | ||||
|  | ||||
| @@ -456,7 +456,7 @@ Instead, propose that: | ||||
| For parity with actions, a `View` would be a constructor which | ||||
| takes an `ActionContext` as a parameter (with similarly-defined | ||||
| properties) and exposes a method to retrieve the HTML elements | ||||
| associateed with it. | ||||
| associated with it. | ||||
|  | ||||
| The platform would then additionally expose an `AngularView` | ||||
| implementation to improve compatibility with existing | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| **Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)* | ||||
|  | ||||
| - [Reducing interface depth (the bundle.json version)](#reducing-interface-depth-the-bundlejson-version) | ||||
|   - [Imperitive component registries](#imperitive-component-registries) | ||||
|   - [Imperitive component registries](#imperative-component-registries) | ||||
|   - [Get rid of "extension category" concept.](#get-rid-of-extension-category-concept) | ||||
|   - [Reduce number and depth of extension points](#reduce-number-and-depth-of-extension-points) | ||||
|   - [Composite services should not be the default](#composite-services-should-not-be-the-default) | ||||
| @@ -30,11 +30,11 @@ | ||||
|  | ||||
| # Reducing interface depth (the bundle.json version) | ||||
|  | ||||
| ## Imperitive component registries  | ||||
| ## Imperative component registries  | ||||
|  | ||||
| Transition component registries to javascript, get rid of bundle.json and bundles.json.  Prescribe a method for application configuration, but allow flexibility in how application configuration is defined. | ||||
|  | ||||
| Register components in an imperitive fashion, see angularApp.factory, angularApp.controller, etc.  Alternatively, implement our own application object with new registries and it's own form of registering objects. | ||||
| Register components in an imperative fashion, see angularApp.factory, angularApp.controller, etc.  Alternatively, implement our own application object with new registries and it's own form of registering objects. | ||||
|  | ||||
| ## Get rid of "extension category" concept. | ||||
|  | ||||
| @@ -99,7 +99,7 @@ To reduce interface depth, we can replace our own provider and registry patterns | ||||
|  | ||||
| ## More angular: for all services | ||||
|  | ||||
| Increasing our commitment to angular would mean using more of the angular factorys, services, etc, and less of our home grown tools.  We'd implement our services and extension points as angular providers, and make them configurable via app.config. | ||||
| Increasing our commitment to angular would mean using more of the angular factories, services, etc, and less of our home grown tools.  We'd implement our services and extension points as angular providers, and make them configurable via app.config. | ||||
|  | ||||
| As an example, registering a specific type of model provider in angular would look like: | ||||
|  | ||||
| @@ -126,9 +126,9 @@ Allow developers to use whatever module loading system they'd like to use, while | ||||
|  | ||||
| ## Use gulp or grunt for standard tooling | ||||
|  | ||||
| Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging.  Additional tools can be added to the workflow at low cost, simplifying the set up of developer environments. | ||||
| Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging.  Additional tools can be added to the workflow at low cost, simplifying the setup of developer environments. | ||||
|  | ||||
| Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compiliation, and ease of extensibility for standard production build processes.  They're key in decoupling code. | ||||
| Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compilation, and ease of extensibility for standard production build processes.  They're key in decoupling code. | ||||
|  | ||||
| ## Package openmctweb as single versioned file. | ||||
|  | ||||
|   | ||||
| @@ -643,7 +643,7 @@ to be passed along by other services. | ||||
| ## Domain Objects  | ||||
|  | ||||
| Domain objects are the most fundamental component of Open MCT's information  | ||||
| model. A domain object is some distinct thing relevant to a user's work flow,  | ||||
| model. A domain object is some distinct thing relevant to a user's workflow,  | ||||
| such as a telemetry channel, display, or similar. Open MCT is a tool for  | ||||
| viewing, browsing, manipulating, and otherwise interacting with a graph of  | ||||
| domain objects.  | ||||
| @@ -933,7 +933,7 @@ Note that `templateUrl` is not supported for `containers`. | ||||
|  | ||||
| Controls provide options for the `mct-control` directive.  | ||||
|   | ||||
| Six standard control types are included in the forms bundle: | ||||
| Ten standard control types are included in the forms bundle: | ||||
|  | ||||
| * `textfield`: An area to enter plain text. | ||||
| * `select`: A drop-down list of options. | ||||
| @@ -941,7 +941,13 @@ Six standard control types are included in the forms bundle: | ||||
| * `color`: A color picker. | ||||
| * `button`: A button. | ||||
| * `datetime`: An input for UTC date/time entry; gives result as a UNIX  | ||||
| timestamp, in milliseconds since start of 1970, UTC.  | ||||
| timestamp, in milliseconds since start of 1970, UTC. | ||||
| * `composite`: A control parenting an array of other controls. | ||||
| * `menu-button`: A drop-down list of items supporting custom behavior | ||||
| on click. | ||||
| * `dialog-button`: A button which opens a dialog allowing a single property | ||||
| to be edited. | ||||
| * `radio`: A radio button. | ||||
|  | ||||
| New controls may be added as extensions of the controls category. Extensions of  | ||||
| this category have two properties:  | ||||
| @@ -981,7 +987,7 @@ Examples of gestures included in the platform are: | ||||
| composition. | ||||
| * `drop`: For representations that can be drop targets for drag-and-drop  | ||||
| composition.  | ||||
| * `menu`: For representations that can be used to pop up a context menu.  | ||||
| * `menu`: For representations that can be used to popup a context menu.  | ||||
|   | ||||
| Gesture definitions have a property `key` which is used as a machine-readable  | ||||
| identifier for the gesture (e.g. `drag`, `drop`, `menu` above.)  | ||||
| @@ -1153,7 +1159,7 @@ For example, the _My Items_ folder is added as an extension of this category. | ||||
|  | ||||
| Extensions of this category should have the following properties: | ||||
|  | ||||
| * `id`: The machine-readable identifier for the domaiwn object being exposed. | ||||
| * `id`: The machine-readable identifier for the domain object being exposed. | ||||
| * `model`: The model, as a JSON object, for the domain object being exposed.  | ||||
|  | ||||
| ## Stylesheets Category | ||||
|   | ||||
| @@ -9,26 +9,29 @@ | ||||
|  | ||||
|  Open MCT provides functionality out of the box, but it's also a platform for | ||||
|  building rich mission operations applications based on modern web technology.  | ||||
|  The platform is configured declaratively, and defines conventions for | ||||
|  building on the provided capabilities by creating modular 'bundles' that  | ||||
|  extend the platform at a variety of extension points. The details of how to | ||||
|  The platform is configured by plugins which extend the platform at a variety | ||||
|  of extension points. The details of how to | ||||
|  extend the platform are provided in the following documentation. | ||||
|  | ||||
| ## Sections | ||||
|  | ||||
|  * The [Architecture Overview](architecture/) describes the concepts used  | ||||
|  throughout Open MCT, and gives a high level overview of the platform's design. | ||||
|   | ||||
|  * The [Developer's Guide](guide/) goes into more detail about how to use the | ||||
|  platform and the functionality that it provides. | ||||
|   | ||||
|  * The [Tutorials](tutorials/) give examples of extending the platform to add  | ||||
|  functionality,  | ||||
|  and integrate with data sources. | ||||
|   | ||||
|  * The [API](api/) document is generated from inline documentation  | ||||
|  using [JSDoc](http://usejsdoc.org/), and describes the JavaScript objects and | ||||
|  functions that make up the software platform. | ||||
|  | ||||
|  * Finally, the [Development Process](process/) document describes the  | ||||
|  * The [Development Process](process/) document describes the | ||||
|  Open MCT software development cycle. | ||||
|  | ||||
| ## Legacy Documentation | ||||
|  | ||||
| As we transition to a new API, the following documentation for the old API | ||||
| (which is supported during the transtion) may be useful as well: | ||||
|  | ||||
|  * The [Architecture Overview](architecture/) describes the concepts used | ||||
|  throughout Open MCT, and gives a high level overview of the platform's design. | ||||
|  | ||||
|  * The [Developer's Guide](guide/) goes into more detail about how to use the | ||||
|  platform and the functionality that it provides. | ||||
|  | ||||
|  * The [Tutorials](tutorials/) give examples of extending the platform to add | ||||
|  functionality, and integrate with data sources. | ||||
|   | ||||
| @@ -102,7 +102,7 @@ perform: | ||||
|  | ||||
| * A relevant subset of [_user testing_](procedures.md#user-test-procedures) | ||||
|   identified by the acting [project manager](../cycle.md#roles). | ||||
| * [_Long-duration testing_](procedures.md#long-duration-testng) | ||||
| * [_Long-duration testing_](procedures.md#long-duration-testing) | ||||
|   (specifically, for 24 hours.) | ||||
|  | ||||
| Issues are reported as a product of both forms of testing. | ||||
|   | ||||
| @@ -48,17 +48,17 @@ for a more general overview of how to run and deploy a Open MCT application. | ||||
|  | ||||
| First step is to check out Open MCT from the source repository.  | ||||
|  | ||||
| `git clone https://github.com/nasa/openmctweb.git openmctweb` | ||||
| `git clone https://github.com/nasa/openmct.git openmct` | ||||
|  | ||||
| This will create a copy of the Open MCT source code repository in the folder  | ||||
| `openmctweb` (relative to the path from which you ran the command.) | ||||
| `openmct` (relative to the path from which you ran the command.) | ||||
| If you have a repository URL, use that as the "path to repo" above. Alternately,  | ||||
| if you received Open MCT as a git bundle, the path to that bundle on the  | ||||
| local filesystem can be used instead. | ||||
| At this point, it will also be useful to branch off of Open MCT v0.6.2  | ||||
| (which was used when writing these tutorials) to begin adding plugins. | ||||
|  | ||||
|     cd openmctweb | ||||
|     cd openmct | ||||
| 	git branch <my branch name> open-v0.6.2 | ||||
| 	git checkout <my branch name> | ||||
|  | ||||
| @@ -79,7 +79,7 @@ changes to stylesheets, or you are running the minified version of the app | ||||
|  | ||||
| The next step is to run a web server so that you can view the Open MCT  | ||||
| client (including the plugins you add to it) in browser. Any web server can  | ||||
| be used for hosting OpenMCTWeb, and a trivial web server is provided in this  | ||||
| be used for hosting Open MCT, and a trivial web server is provided in this  | ||||
| package for the purposes of running the tutorials. The provided web server  | ||||
| should not be used in a production environment | ||||
|    | ||||
| @@ -91,7 +91,7 @@ To run the tutorial web server | ||||
|  | ||||
| Once running, you should be able to view Open MCT from your browser at  | ||||
| http://localhost:8080/ (assuming the web server is running on port 8080,  | ||||
| and OpenMCTWeb is installed at the server's root path).  | ||||
| and Open MCT is installed at the server's root path).  | ||||
| [Google Chrome](https://www.google.com/chrome/) is recommended for these  | ||||
| tutorials, as Chrome is Open MCT's "test-to" browser. The browser cache  | ||||
| can sometimes interfere with development (masking changes by  | ||||
| @@ -130,7 +130,6 @@ to this plugin as tutorials/todo as well.) We will start with an "empty bundle", | ||||
| one which exposes no extensions - which looks like: | ||||
|  | ||||
| ```diff | ||||
|  | ||||
| define([ | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
| @@ -144,7 +143,6 @@ define([ | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| ``` | ||||
| __tutorials/todo/bundle.js__ | ||||
|  | ||||
| @@ -348,7 +346,8 @@ deeper explanation of domain objects, see the Open MCT Developer Guide.) | ||||
| In the case of our to-do list feature, the to-do list itself is the thing we'll  | ||||
| want users to be able to create and edit. So, we will add that as a new type in  | ||||
| our bundle definition: | ||||
| ```diff     | ||||
|  | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
| @@ -370,7 +369,6 @@ define([ | ||||
| +       ]} | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| ``` | ||||
| __tutorials/todo/bundle.js__ | ||||
|  | ||||
| @@ -427,7 +425,6 @@ are stored by convention.) | ||||
|     </li> | ||||
| </ul> | ||||
| ``` | ||||
|  | ||||
| __tutorials/todo/res/templates/todo.html__ | ||||
|  | ||||
| A summary of what's included: | ||||
| @@ -573,6 +570,7 @@ We will define that in an AMD module (see http://requirejs.org/docs/whyamd.html) | ||||
| in the directory `tutorials/todo/src/controllers` (`src` is, by default, the  | ||||
| directory where bundle-related source code is kept, and controllers is where  | ||||
| Angular controllers are stored by convention.) | ||||
|  | ||||
| ```diff | ||||
| define(function () { | ||||
|     function TodoController($scope) { | ||||
| @@ -971,6 +969,7 @@ by the tool bar we've defined. | ||||
| Additionally, we need to make changes to our template to select specific tasks  | ||||
| in response to some user gesture. Here, we will select tasks when a user clicks  | ||||
| the description. | ||||
|  | ||||
| ```diff | ||||
| <div ng-controller="TodoController"> | ||||
|     <div> | ||||
| @@ -996,6 +995,7 @@ __tutorials/todo/res/templates/todo.html__ | ||||
|  | ||||
| Finally, the `TodoController` uses the `dialogService` now, so we need to  | ||||
| declare that dependency in its extension definition: | ||||
|  | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry', | ||||
| @@ -1248,7 +1248,6 @@ another file to the res directory of our bundle; this time, it is `css/todo.css` | ||||
|     font-style: italic; | ||||
| } | ||||
| ``` | ||||
|  | ||||
| __tutorials/todo/res/css/todo.css__ | ||||
|  | ||||
| Here, we have defined classes and appearances for: | ||||
| @@ -1261,6 +1260,7 @@ Here, we have defined classes and appearances for: | ||||
| To include this CSS file in our running instance of Open MCT, we need to  | ||||
| declare it in our bundle definition, this time as an extension of category  | ||||
| `stylesheets`: | ||||
|  | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry', | ||||
| @@ -1430,7 +1430,6 @@ define([ | ||||
|     }); | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| __tutorials/bargraph/bundle.js__ | ||||
|  | ||||
| The view definition should look familiar after the To-Do List tutorial, with  | ||||
| @@ -1499,6 +1498,7 @@ The third is for labels along the horizontal axis, which will indicate which | ||||
| bar corresponds to which telemetry point. Inline `style` attributes are used  | ||||
| wherever dynamic positioning (handled by a script) is anticipated. | ||||
| The corresponding CSS file which styles and positions these elements: | ||||
|  | ||||
| ```diff | ||||
| .example-bargraph { | ||||
|     position: absolute; | ||||
| @@ -1596,6 +1596,7 @@ actual telemetry data in subsequent steps.) | ||||
| Notably, we will not try to show telemetry data after this step. | ||||
|  | ||||
| To support this, we will add a new controller which supports our Bar Graph view: | ||||
|  | ||||
| ```diff | ||||
| define(function () { | ||||
|     function BarGraphController($scope, telemetryHandler) { | ||||
| @@ -1647,6 +1648,7 @@ Whenever the telemetry handler invokes its callbacks, we update the set of | ||||
| telemetry objects in view, as well as the width for each bar. | ||||
|  | ||||
| We will also utilize this from our template: | ||||
|  | ||||
| ```diff | ||||
| + <div class="example-bargraph" ng-controller="BarGraphController"> | ||||
|     <div class="example-tick-labels"> | ||||
| @@ -2346,6 +2348,7 @@ add a top-level object which will serve as a container; in the next step, we | ||||
| will populate this with the contents of the telemetry dictionary (which we  | ||||
| will retrieve from the server.) | ||||
|  | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
| @@ -2375,6 +2378,7 @@ define([ | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
| ``` | ||||
| __tutorials/telemetry/bundle.js__ | ||||
|  | ||||
| Here, we've created our initial telemetry plugin. This exposes a new domain  | ||||
| @@ -2469,7 +2473,6 @@ define([ | ||||
|     }; | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| __main.js__ | ||||
|  | ||||
| ...we will be able to reload Open MCT and see that it is present: | ||||
| @@ -2486,43 +2489,45 @@ server. Our first step will be to add a service that will handle interactions | ||||
| with the server; this will not be used by Open MCT directly, but will be  | ||||
| used by subsequent components we add. | ||||
|  | ||||
|     /*global define,WebSocket*/ | ||||
|      | ||||
|     define( | ||||
|         [], | ||||
|         function () { | ||||
|             "use strict"; | ||||
|      | ||||
|             function ExampleTelemetryServerAdapter($q, wsUrl) { | ||||
|                 var ws = new WebSocket(wsUrl), | ||||
|                     dictionary = $q.defer(); | ||||
|      | ||||
|                 // Handle an incoming message from the server | ||||
|                 ws.onmessage = function (event) { | ||||
|                     var message = JSON.parse(event.data); | ||||
|      | ||||
|                     switch (message.type) { | ||||
|                     case "dictionary": | ||||
|                         dictionary.resolve(message.value); | ||||
|                         break; | ||||
|                     } | ||||
|                 }; | ||||
|      | ||||
|                 // Request dictionary once connection is established | ||||
|                 ws.onopen = function () { | ||||
|                     ws.send("dictionary"); | ||||
|                 }; | ||||
|      | ||||
|                 return { | ||||
|                     dictionary: function () { | ||||
|                         return dictionary.promise; | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|      | ||||
|             return ExampleTelemetryServerAdapter; | ||||
| ```diff | ||||
| /*global define,WebSocket*/ | ||||
|  | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         function ExampleTelemetryServerAdapter($q, wsUrl) { | ||||
|             var ws = new WebSocket(wsUrl), | ||||
|                 dictionary = $q.defer(); | ||||
|  | ||||
|             // Handle an incoming message from the server | ||||
|             ws.onmessage = function (event) { | ||||
|                 var message = JSON.parse(event.data); | ||||
|  | ||||
|                 switch (message.type) { | ||||
|                 case "dictionary": | ||||
|                     dictionary.resolve(message.value); | ||||
|                     break; | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             // Request dictionary once connection is established | ||||
|             ws.onopen = function () { | ||||
|                 ws.send("dictionary"); | ||||
|             }; | ||||
|  | ||||
|             return { | ||||
|                 dictionary: function () { | ||||
|                     return dictionary.promise; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|         return ExampleTelemetryServerAdapter; | ||||
|     } | ||||
| ); | ||||
| ``` | ||||
| __tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ | ||||
|  | ||||
| When created, this service initiates a connection to the server, and begins  | ||||
| @@ -2539,86 +2544,88 @@ subsystems. This means that we need to convert the data from the dictionary | ||||
| into domain object models, and expose these to Open MCT via a  | ||||
| `modelService`. | ||||
|  | ||||
|     /*global define*/ | ||||
|      | ||||
|     define( | ||||
|         function () { | ||||
|             "use strict"; | ||||
|      | ||||
|             var PREFIX = "example_tlm:", | ||||
|                 FORMAT_MAPPINGS = { | ||||
|                     float: "number", | ||||
|                     integer: "number", | ||||
|                     string: "string" | ||||
|                 }; | ||||
|      | ||||
|             function ExampleTelemetryModelProvider(adapter, $q) { | ||||
|                 var modelPromise, empty = $q.when({}); | ||||
|      | ||||
|                 // Check if this model is in our dictionary (by prefix) | ||||
|                 function isRelevant(id) { | ||||
|                     return id.indexOf(PREFIX) === 0; | ||||
|                 } | ||||
|      | ||||
|                 // Build a domain object identifier by adding a prefix | ||||
|                 function makeId(element) { | ||||
|                     return PREFIX + element.identifier; | ||||
|                 } | ||||
|      | ||||
|                 // Create domain object models from this dictionary | ||||
|                 function buildTaxonomy(dictionary) { | ||||
|                     var models = {}; | ||||
|      | ||||
|                     // Create & store a domain object model for a measurement | ||||
|                     function addMeasurement(measurement) { | ||||
|                         var format = FORMAT_MAPPINGS[measurement.type]; | ||||
|                         models[makeId(measurement)] = { | ||||
|                             type: "example.measurement", | ||||
|                             name: measurement.name, | ||||
|                             telemetry: { | ||||
|                                 key: measurement.identifier, | ||||
|                                 ranges: [{ | ||||
|                                     key: "value", | ||||
|                                     name: "Value", | ||||
|                                     units: measurement.units, | ||||
|                                     format: format | ||||
|                                 }] | ||||
|                             } | ||||
|                         }; | ||||
|                     } | ||||
|      | ||||
|                     // Create & store a domain object model for a subsystem | ||||
|                     function addSubsystem(subsystem) { | ||||
|                         var measurements = | ||||
|                             (subsystem.measurements || []); | ||||
|                         models[makeId(subsystem)] = { | ||||
|                             type: "example.subsystem", | ||||
|                             name: subsystem.name, | ||||
|                             composition: measurements.map(makeId) | ||||
|                         }; | ||||
|                         measurements.forEach(addMeasurement); | ||||
|                     } | ||||
|      | ||||
|                     (dictionary.subsystems || []).forEach(addSubsystem); | ||||
|      | ||||
|                     return models; | ||||
|                 } | ||||
|      | ||||
|                 // Begin generating models once the dictionary is available | ||||
|                 modelPromise = adapter.dictionary().then(buildTaxonomy); | ||||
|      | ||||
|                 return { | ||||
|                     getModels: function (ids) { | ||||
|                         // Return models for the dictionary only when they | ||||
|                         // are relevant to the request. | ||||
|                         return ids.some(isRelevant) ? modelPromise : empty; | ||||
|                     } | ||||
|                 }; | ||||
| ```diff | ||||
| /*global define*/ | ||||
|  | ||||
| define( | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         var PREFIX = "example_tlm:", | ||||
|             FORMAT_MAPPINGS = { | ||||
|                 float: "number", | ||||
|                 integer: "number", | ||||
|                 string: "string" | ||||
|             }; | ||||
|  | ||||
|         function ExampleTelemetryModelProvider(adapter, $q) { | ||||
|             var modelPromise, empty = $q.when({}); | ||||
|  | ||||
|             // Check if this model is in our dictionary (by prefix) | ||||
|             function isRelevant(id) { | ||||
|                 return id.indexOf(PREFIX) === 0; | ||||
|             } | ||||
|      | ||||
|             return ExampleTelemetryModelProvider; | ||||
|  | ||||
|             // Build a domain object identifier by adding a prefix | ||||
|             function makeId(element) { | ||||
|                 return PREFIX + element.identifier; | ||||
|             } | ||||
|  | ||||
|             // Create domain object models from this dictionary | ||||
|             function buildTaxonomy(dictionary) { | ||||
|                 var models = {}; | ||||
|  | ||||
|                 // Create & store a domain object model for a measurement | ||||
|                 function addMeasurement(measurement) { | ||||
|                     var format = FORMAT_MAPPINGS[measurement.type]; | ||||
|                     models[makeId(measurement)] = { | ||||
|                         type: "example.measurement", | ||||
|                         name: measurement.name, | ||||
|                         telemetry: { | ||||
|                             key: measurement.identifier, | ||||
|                             ranges: [{ | ||||
|                                 key: "value", | ||||
|                                 name: "Value", | ||||
|                                 units: measurement.units, | ||||
|                                 format: format | ||||
|                             }] | ||||
|                         } | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 // Create & store a domain object model for a subsystem | ||||
|                 function addSubsystem(subsystem) { | ||||
|                     var measurements = | ||||
|                         (subsystem.measurements || []); | ||||
|                     models[makeId(subsystem)] = { | ||||
|                         type: "example.subsystem", | ||||
|                         name: subsystem.name, | ||||
|                         composition: measurements.map(makeId) | ||||
|                     }; | ||||
|                     measurements.forEach(addMeasurement); | ||||
|                 } | ||||
|  | ||||
|                 (dictionary.subsystems || []).forEach(addSubsystem); | ||||
|  | ||||
|                 return models; | ||||
|             } | ||||
|  | ||||
|             // Begin generating models once the dictionary is available | ||||
|             modelPromise = adapter.dictionary().then(buildTaxonomy); | ||||
|  | ||||
|             return { | ||||
|                 getModels: function (ids) { | ||||
|                     // Return models for the dictionary only when they | ||||
|                     // are relevant to the request. | ||||
|                     return ids.some(isRelevant) ? modelPromise : empty; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|         return ExampleTelemetryModelProvider; | ||||
|     } | ||||
| ); | ||||
| ``` | ||||
| __tutorials/telemetry/src/ExampleTelemetryModelProvider.js__ | ||||
|  | ||||
| This script implements a `provider` for `modelService`; the `modelService` is a  | ||||
| @@ -2671,55 +2678,57 @@ This allows our telemetry dictionary to be expressed as domain object models | ||||
| fix this, we will need another script which will add these subsystems to the  | ||||
| root-level object we added in Step 1. | ||||
|  | ||||
|     /*global define*/ | ||||
|      | ||||
|     define( | ||||
|         function () { | ||||
|             "use strict"; | ||||
|      | ||||
|             var TAXONOMY_ID = "example:sc", | ||||
|                 PREFIX = "example_tlm:"; | ||||
|      | ||||
|             function ExampleTelemetryInitializer(adapter, objectService) { | ||||
|                 // Generate a domain object identifier for a dictionary element | ||||
|                 function makeId(element) { | ||||
|                     return PREFIX + element.identifier; | ||||
|                 } | ||||
|      | ||||
|                 // When the dictionary is available, add all subsystems | ||||
|                 // to the composition of My Spacecraft | ||||
|                 function initializeTaxonomy(dictionary) { | ||||
|                     // Get the top-level container for dictionary objects | ||||
|                     // from a group of domain objects. | ||||
|                     function getTaxonomyObject(domainObjects) { | ||||
|                         return domainObjects[TAXONOMY_ID]; | ||||
|                     } | ||||
|      | ||||
|                     // Populate | ||||
|                     function populateModel(taxonomyObject) { | ||||
|                         return taxonomyObject.useCapability( | ||||
|                             "mutation", | ||||
|                             function (model) { | ||||
|                                 model.name = | ||||
|                                     dictionary.name; | ||||
|                                 model.composition = | ||||
|                                     dictionary.subsystems.map(makeId); | ||||
|                             } | ||||
|                         ); | ||||
|                     } | ||||
|      | ||||
|                     // Look up My Spacecraft, and populate it accordingly. | ||||
|                     objectService.getObjects([TAXONOMY_ID]) | ||||
|                         .then(getTaxonomyObject) | ||||
|                         .then(populateModel); | ||||
|                 } | ||||
|      | ||||
|                 adapter.dictionary().then(initializeTaxonomy); | ||||
| ```diff | ||||
| /*global define*/ | ||||
|  | ||||
| define( | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         var TAXONOMY_ID = "example:sc", | ||||
|             PREFIX = "example_tlm:"; | ||||
|  | ||||
|         function ExampleTelemetryInitializer(adapter, objectService) { | ||||
|             // Generate a domain object identifier for a dictionary element | ||||
|             function makeId(element) { | ||||
|                 return PREFIX + element.identifier; | ||||
|             } | ||||
|      | ||||
|             return ExampleTelemetryInitializer; | ||||
|  | ||||
|             // When the dictionary is available, add all subsystems | ||||
|             // to the composition of My Spacecraft | ||||
|             function initializeTaxonomy(dictionary) { | ||||
|                 // Get the top-level container for dictionary objects | ||||
|                 // from a group of domain objects. | ||||
|                 function getTaxonomyObject(domainObjects) { | ||||
|                     return domainObjects[TAXONOMY_ID]; | ||||
|                 } | ||||
|  | ||||
|                 // Populate | ||||
|                 function populateModel(taxonomyObject) { | ||||
|                     return taxonomyObject.useCapability( | ||||
|                         "mutation", | ||||
|                         function (model) { | ||||
|                             model.name = | ||||
|                                 dictionary.name; | ||||
|                             model.composition = | ||||
|                                 dictionary.subsystems.map(makeId); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|  | ||||
|                 // Look up My Spacecraft, and populate it accordingly. | ||||
|                 objectService.getObjects([TAXONOMY_ID]) | ||||
|                     .then(getTaxonomyObject) | ||||
|                     .then(populateModel); | ||||
|             } | ||||
|  | ||||
|             adapter.dictionary().then(initializeTaxonomy); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|         return ExampleTelemetryInitializer; | ||||
|     } | ||||
| ); | ||||
| ``` | ||||
| __tutorials/telemetry/src/ExampleTelemetryInitializer.js__ | ||||
|  | ||||
| At the conclusion of Step 1, the top-level My Spacecraft object was empty. This  | ||||
| @@ -2737,14 +2746,14 @@ with the platform): | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry', | ||||
|     './src/ExampleTelemetryServerAdapter', | ||||
|     './src/ExampleTelemetryInitializer', | ||||
|     './src/ExampleTelemetryModelProvider' | ||||
| +   './src/ExampleTelemetryServerAdapter', | ||||
| +   './src/ExampleTelemetryInitializer', | ||||
| +   './src/ExampleTelemetryModelProvider' | ||||
| ], function ( | ||||
|     legacyRegistry, | ||||
|     ExampleTelemetryServerAdapter, | ||||
|     ExampleTelemetryInitializer, | ||||
|     ExampleTelemetryModelProvider | ||||
| +   ExampleTelemetryServerAdapter, | ||||
| +   ExampleTelemetryInitializer, | ||||
| +   ExampleTelemetryModelProvider | ||||
| ) { | ||||
|     legacyRegistry.register("tutorials/telemetry", { | ||||
|     "name": "Example Telemetry Adapter", | ||||
| @@ -2755,7 +2764,7 @@ define([ | ||||
|                 "key": "example.spacecraft", | ||||
|                 "glyph": "o" | ||||
|             }, | ||||
|             { | ||||
| +           { | ||||
| +               "name": "Subsystem", | ||||
| +               "key": "example.subsystem", | ||||
| +               "glyph": "o", | ||||
| @@ -2934,6 +2943,7 @@ identifier, the pending promise is resolved. | ||||
|  | ||||
| This `history` method will be used by a `telemetryService` provider which we  | ||||
| will implement: | ||||
|  | ||||
| ```diff | ||||
| /*global define*/ | ||||
|  | ||||
| @@ -3019,6 +3029,7 @@ Finally, note that we also have a `subscribe` method, to satisfy the interface o | ||||
| `telemetryService`, but this `subscribe` method currently does nothing. | ||||
|  | ||||
| This script uses an `ExampleTelemetrySeries` class, which looks like: | ||||
|  | ||||
| ```diff | ||||
| /*global define*/ | ||||
|  | ||||
| @@ -3050,6 +3061,7 @@ This takes the array of telemetry values (as returned by the server) and wraps | ||||
| it with the interface expected by the platform (the methods shown.) | ||||
|  | ||||
| Finally, we expose this `telemetryService` provider declaratively: | ||||
|  | ||||
| ```diff | ||||
| define([ | ||||
|     'legacyRegistry', | ||||
| @@ -3226,7 +3238,7 @@ define( | ||||
| __tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ | ||||
|  | ||||
| Here, we have added `subscribe` and `unsubscribe` methods which issue the  | ||||
| corresponding requests to the server. Seperately, we introduce the ability to  | ||||
| corresponding requests to the server. Separately, we introduce the ability to  | ||||
| listen for `data` messages as they come in: These will contain the data associated  | ||||
| with these subscriptions. | ||||
|  | ||||
| @@ -3316,7 +3328,6 @@ define( | ||||
|     } | ||||
| ); | ||||
| ``` | ||||
|      | ||||
| __tutorials/telemetry/src/ExampleTelemetryProvider.js__ | ||||
|  | ||||
| A quick summary of these changes: | ||||
|   | ||||
| @@ -49,7 +49,7 @@ define([ | ||||
|                 { | ||||
|                     "key": "eventGenerator", | ||||
|                     "name": "Event Message Generator", | ||||
|                     "glyph": "\u0066", | ||||
|                     "cssclass": "icon-folder-new", | ||||
|                     "description": "For development use. Creates sample event message data that mimics a live data stream.", | ||||
|                     "priority": 10, | ||||
|                     "features": "creation", | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|     "LMP: 47 degrees.", | ||||
|     "CC: Eagle, looking great. You're GO.", | ||||
|     "CC: Roger. 1202. We copy it.", | ||||
|     "O1: LMP 35 degrees. 35 degrees. 750. Coming aown to 23.fl", | ||||
|     "O1: LMP 35 degrees. 35 degrees. 750. Coming down to 23.fl", | ||||
|     "LMP: 700 feet, 21 down, 33 degrees.", | ||||
|     "LMP: 600 feet, down at 19.", | ||||
|     "LMP: 540 feet, down at - 30. Down at 15.", | ||||
|   | ||||
| @@ -36,7 +36,7 @@ define([ | ||||
|                     "name": "Export Telemetry as CSV", | ||||
|                     "implementation": ExportTelemetryAsCSVAction, | ||||
|                     "category": "contextual", | ||||
|                     "glyph": "\u0033", | ||||
|                     "cssclass": "icon-download", | ||||
|                     "depends": [ "exportService" ] | ||||
|                 } | ||||
|             ] | ||||
|   | ||||
| @@ -20,12 +20,14 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div ng-controller="ExampleFormController"> | ||||
|     <mct-toolbar structure="toolbar" ng-model="state" name="aToolbar"> | ||||
|     </mct-toolbar> | ||||
|  | ||||
|     <mct-form structure="form" ng-model="state" name="aForm"> | ||||
|     </mct-form> | ||||
|     <mct-toolbar structure="toolbar" | ||||
|                  ng-model="state" | ||||
|                  name="aToolbar"></mct-toolbar> | ||||
|  | ||||
|     <mct-form structure="form" | ||||
|               ng-model="state" | ||||
|               class="validates" | ||||
|               name="aForm"></mct-form> | ||||
|  | ||||
|     <ul> | ||||
|         <li>Dirty: {{aForm.$dirty}}</li> | ||||
| @@ -33,11 +35,8 @@ | ||||
|     </ul> | ||||
|  | ||||
|     <pre> | ||||
|  | ||||
|  | ||||
|     <textarea> | ||||
|     {{state | json}} | ||||
|     </textarea> | ||||
|  | ||||
|         <textarea> | ||||
|             {{state | json}} | ||||
|         </textarea> | ||||
|     </pre> | ||||
| </div> | ||||
| @@ -78,27 +78,26 @@ define( | ||||
|                         items: [ | ||||
|                             { | ||||
|                                 control: "button", | ||||
|                                 glyph: "1", | ||||
|                                 description: "Button A", | ||||
|                                 csslass: "icon-save", | ||||
|                                 click: function () { | ||||
|                                     window.alert("A"); | ||||
|                                     window.alert("Save"); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 control: "button", | ||||
|                                 glyph: "2", | ||||
|                                 csslass: "icon-x", | ||||
|                                 description: "Button B", | ||||
|                                 click: function () { | ||||
|                                     window.alert("B"); | ||||
|                                     window.alert("Cancel"); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 control: "button", | ||||
|                                 glyph: "3", | ||||
|                                 csslass: "icon-trash", | ||||
|                                 description: "Button C", | ||||
|                                 disabled: true, | ||||
|                                 click: function () { | ||||
|                                     window.alert("C"); | ||||
|                                     window.alert("Delete"); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|   | ||||
| @@ -86,7 +86,7 @@ define([ | ||||
|                 { | ||||
|                     "key": "generator", | ||||
|                     "name": "Sine Wave Generator", | ||||
|                     "glyph": "\u0054", | ||||
|                     "cssclass": "icon-telemetry", | ||||
|                     "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", | ||||
|                     "priority": 10, | ||||
|                     "features": "creation", | ||||
|   | ||||
| @@ -49,7 +49,7 @@ define([ | ||||
|                 { | ||||
|                     "key": "imagery", | ||||
|                     "name": "Example Imagery", | ||||
|                     "glyph": "\u00e3", | ||||
|                     "cssclass": "icon-image", | ||||
|                     "features": "creation", | ||||
|                     "description": "For development use. Creates example imagery data that mimics a live imagery stream.", | ||||
|                     "priority": 10, | ||||
|   | ||||
							
								
								
									
										48
									
								
								example/localTimeSystem/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								example/localTimeSystem/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     "./src/LocalTimeSystem", | ||||
|     "./src/LocalTimeFormat", | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     LocalTimeSystem, | ||||
|     LocalTimeFormat, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     legacyRegistry.register("example/localTimeSystem", { | ||||
|         "extensions": { | ||||
|             "formats": [ | ||||
|                 { | ||||
|                     "key": "local-format", | ||||
|                     "implementation": LocalTimeFormat | ||||
|                 } | ||||
|             ], | ||||
|             "timeSystems": [ | ||||
|                 { | ||||
|                     "implementation": LocalTimeSystem, | ||||
|                     "depends": ["$timeout"] | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										43
									
								
								example/localTimeSystem/src/LADTickSource.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								example/localTimeSystem/src/LADTickSource.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock'], function (LocalClock) { | ||||
|     /** | ||||
|      * @implements TickSource | ||||
|      * @constructor | ||||
|      */ | ||||
|     function LADTickSource ($timeout, period) { | ||||
|         LocalClock.call(this, $timeout, period); | ||||
|  | ||||
|         this.metadata = { | ||||
|             key: 'test-lad', | ||||
|             mode: 'lad', | ||||
|             cssclass: 'icon-clock', | ||||
|             label: 'Latest Available Data', | ||||
|             name: 'Latest available data', | ||||
|             description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.' | ||||
|         }; | ||||
|     } | ||||
|     LADTickSource.prototype = Object.create(LocalClock.prototype); | ||||
|  | ||||
|     return LADTickSource; | ||||
| }); | ||||
							
								
								
									
										112
									
								
								example/localTimeSystem/src/LocalTimeFormat.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								example/localTimeSystem/src/LocalTimeFormat.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'moment' | ||||
| ], function ( | ||||
|     moment | ||||
| ) { | ||||
|  | ||||
|     var DATE_FORMAT = "YYYY-MM-DD h:mm:ss.SSS a", | ||||
|         DATE_FORMATS = [ | ||||
|             DATE_FORMAT, | ||||
|             "YYYY-MM-DD h:mm:ss a", | ||||
|             "YYYY-MM-DD h:mm a", | ||||
|             "YYYY-MM-DD" | ||||
|         ]; | ||||
|  | ||||
|     /** | ||||
|      * @typedef Scale | ||||
|      * @property {number} min the minimum scale value, in ms | ||||
|      * @property {number} max the maximum scale value, in ms | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Formatter for UTC timestamps. Interprets numeric values as | ||||
|      * milliseconds since the start of 1970. | ||||
|      * | ||||
|      * @implements {Format} | ||||
|      * @constructor | ||||
|      * @memberof platform/commonUI/formats | ||||
|      */ | ||||
|     function LocalTimeFormat() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an appropriate time format based on the provided value and | ||||
|      * the threshold required. | ||||
|      * @private | ||||
|      */ | ||||
|     function getScaledFormat (d) { | ||||
|         var m = moment.utc(d); | ||||
|         /** | ||||
|          * Uses logic from d3 Time-Scales, v3 of the API. See | ||||
|          * https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md | ||||
|          * | ||||
|          * Licensed | ||||
|          */ | ||||
|         return [ | ||||
|             [".SSS", function(m) { return m.milliseconds(); }], | ||||
|             [":ss", function(m) { return m.seconds(); }], | ||||
|             ["hh:mma", function(m) { return m.minutes(); }], | ||||
|             ["hha", function(m) { return m.hours(); }], | ||||
|             ["ddd DD", function(m) { | ||||
|                 return m.days() && | ||||
|                     m.date() != 1; | ||||
|             }], | ||||
|             ["MMM DD", function(m) { return m.date() != 1; }], | ||||
|             ["MMMM", function(m) { | ||||
|                 return m.month(); | ||||
|             }], | ||||
|             ["YYYY", function() { return true; }] | ||||
|         ].filter(function (row){ | ||||
|             return row[1](m); | ||||
|         })[0][0]; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param value | ||||
|      * @param {Scale} [scale] Optionally provides context to the | ||||
|      * format request, allowing for scale-appropriate formatting. | ||||
|      * @returns {string} the formatted date | ||||
|      */ | ||||
|     LocalTimeFormat.prototype.format = function (value, scale) { | ||||
|         if (scale !== undefined){ | ||||
|             var scaledFormat = getScaledFormat(value, scale); | ||||
|             if (scaledFormat) { | ||||
|                 return moment.utc(value).format(scaledFormat); | ||||
|             } | ||||
|         } | ||||
|         return moment(value).format(DATE_FORMAT); | ||||
|     }; | ||||
|  | ||||
|     LocalTimeFormat.prototype.parse = function (text) { | ||||
|         return moment(text, DATE_FORMATS).valueOf(); | ||||
|     }; | ||||
|  | ||||
|     LocalTimeFormat.prototype.validate = function (text) { | ||||
|         return moment(text, DATE_FORMATS).isValid(); | ||||
|     }; | ||||
|  | ||||
|     return LocalTimeFormat; | ||||
| }); | ||||
							
								
								
									
										79
									
								
								example/localTimeSystem/src/LocalTimeSystem.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								example/localTimeSystem/src/LocalTimeSystem.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT Web includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     '../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem', | ||||
|     '../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock', | ||||
|     './LADTickSource' | ||||
| ], function (TimeSystem, LocalClock, LADTickSource) { | ||||
|     var THIRTY_MINUTES = 30 * 60 * 1000, | ||||
|         DEFAULT_PERIOD = 1000; | ||||
|  | ||||
|     /** | ||||
|      * This time system supports UTC dates and provides a ticking clock source. | ||||
|      * @implements TimeSystem | ||||
|      * @constructor | ||||
|      */ | ||||
|     function LocalTimeSystem ($timeout) { | ||||
|         TimeSystem.call(this); | ||||
|  | ||||
|         /** | ||||
|          * Some metadata, which will be used to identify the time system in | ||||
|          * the UI | ||||
|          * @type {{key: string, name: string, glyph: string}} | ||||
|          */ | ||||
|         this.metadata = { | ||||
|             'key': 'local', | ||||
|             'name': 'Local', | ||||
|             'glyph': '\u0043' | ||||
|         }; | ||||
|  | ||||
|         this.fmts = ['local-format']; | ||||
|         this.sources = [new LocalClock($timeout, DEFAULT_PERIOD), new LADTickSource($timeout, DEFAULT_PERIOD)]; | ||||
|     } | ||||
|  | ||||
|     LocalTimeSystem.prototype = Object.create(TimeSystem.prototype); | ||||
|  | ||||
|     LocalTimeSystem.prototype.formats = function () { | ||||
|         return this.fmts; | ||||
|     }; | ||||
|  | ||||
|     LocalTimeSystem.prototype.deltaFormat = function () { | ||||
|         return 'duration'; | ||||
|     }; | ||||
|  | ||||
|     LocalTimeSystem.prototype.tickSources = function () { | ||||
|         return this.sources; | ||||
|     }; | ||||
|  | ||||
|     LocalTimeSystem.prototype.defaults = function (key) { | ||||
|         var now = Math.ceil(Date.now() / 1000) * 1000; | ||||
|         return { | ||||
|             key: 'local-default', | ||||
|             name: 'Local 12 hour time system defaults', | ||||
|             deltas: {start: THIRTY_MINUTES, end: 0}, | ||||
|             bounds: {start: now - THIRTY_MINUTES, end: now} | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     return LocalTimeSystem; | ||||
| }); | ||||
| @@ -25,7 +25,7 @@ | ||||
|  | ||||
| @include phoneandtablet { | ||||
|     // Show the Create button | ||||
|     .create-btn-holder { | ||||
|     .create-button-holder { | ||||
|         display: block !important; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,25 +36,25 @@ define([ | ||||
|     legacyRegistry | ||||
| ) { | ||||
|     "use strict"; | ||||
|     legacyRegistry.register("example/notifications", { | ||||
|     legacyRegistry.register("example/msl-adapter", { | ||||
|         "name" : "Mars Science Laboratory Data Adapter", | ||||
|         "extensions" : { | ||||
|         "types": [ | ||||
|             { | ||||
|                 "name":"Mars Science Laboratory", | ||||
|                 "key": "msl.curiosity", | ||||
|                 "glyph": "o" | ||||
|                 "cssclass": "icon-object" | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Instrument", | ||||
|                 "key": "msl.instrument", | ||||
|                 "glyph": "o", | ||||
|                 "cssclass": "icon-object", | ||||
|                 "model": {"composition": []} | ||||
|             }, | ||||
|             { | ||||
|                 "name": "Measurement", | ||||
|                 "key": "msl.measurement", | ||||
|                 "glyph": "\u0054", | ||||
|                 "cssclass": "icon-telemetry", | ||||
|                 "model": {"telemetry": {}}, | ||||
|                 "telemetry": { | ||||
|                     "source": "rems.source", | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <span class="status block ok" ng-controller="DialogLaunchController"> | ||||
| <span class="status block" ng-controller="DialogLaunchController"> | ||||
|     <!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! --> | ||||
|     <span class="ui-symbol status-indicator"></span><span class="label"> | ||||
|     <span class="status-indicator icon-box-with-arrow"></span><span class="label"> | ||||
|         <a ng-click="launchProgress(true)">Known</a> | | ||||
|         <a ng-click="launchProgress(false)">Unknown</a> | | ||||
|         <a ng-click="launchError()">Error</a> | | ||||
|         <a ng-click="launchInfo()">Info</a> | ||||
|     </span><span class="count">Dialogs</span> | ||||
|     </span><span class="count"></span> | ||||
| </span> | ||||
| @@ -1,9 +1,9 @@ | ||||
| <span class="status block ok" ng-controller="NotificationLaunchController"> | ||||
| <span class="status block" ng-controller="NotificationLaunchController"> | ||||
|     <!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! --> | ||||
|     <span class="ui-symbol status-indicator"></span><span class="label"> | ||||
|     <span class="status-indicator icon-bell"></span><span class="label"> | ||||
|         <a ng-click="newInfo()">Success</a> | | ||||
|         <a ng-click="newError()">Error</a> | | ||||
|         <a ng-click="newAlert()">Alert</a> | | ||||
|         <a ng-click="newProgress()">Progress</a> | ||||
|     </span><span class="count">Notifications</span> | ||||
|     </span><span class="count"></span> | ||||
| </span> | ||||
| @@ -32,17 +32,15 @@ define( | ||||
|          * launched for demonstration and testing purposes. | ||||
|          * @constructor | ||||
|          */ | ||||
|  | ||||
|         function DialogLaunchIndicator() { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         DialogLaunchIndicator.template = 'dialogLaunchTemplate'; | ||||
|  | ||||
|         DialogLaunchIndicator.prototype.getGlyph = function () { | ||||
|             return "i"; | ||||
|         }; | ||||
|         DialogLaunchIndicator.prototype.getGlyphClass = function () { | ||||
|             return 'caution'; | ||||
|             return 'ok'; | ||||
|         }; | ||||
|         DialogLaunchIndicator.prototype.getText = function () { | ||||
|             return "Launch test dialog"; | ||||
|   | ||||
| @@ -26,17 +26,21 @@ define( | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
|          * A tool for manually invoking notifications. When included this | ||||
|          * indicator will allow for notifications of different types to be | ||||
|          * launched for demonstration and testing purposes. | ||||
|          * @constructor | ||||
|          */ | ||||
|  | ||||
|         function NotificationLaunchIndicator() { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         NotificationLaunchIndicator.template = 'notificationLaunchTemplate'; | ||||
|  | ||||
|         NotificationLaunchIndicator.prototype.getGlyph = function () { | ||||
|             return "i"; | ||||
|         }; | ||||
|         NotificationLaunchIndicator.prototype.getGlyphClass = function () { | ||||
|             return 'caution'; | ||||
|             return 'ok'; | ||||
|         }; | ||||
|         NotificationLaunchIndicator.prototype.getText = function () { | ||||
|             return "Launch notification"; | ||||
|   | ||||
| @@ -81,7 +81,7 @@ define([ | ||||
|                 { | ||||
|                     "key": "plot", | ||||
|                     "name": "Example Telemetry Plot", | ||||
|                     "glyph": "\u0074", | ||||
|                     "cssclass": "icon-telemetry-panel", | ||||
|                     "description": "For development use. A plot for displaying telemetry.", | ||||
|                     "priority": 10, | ||||
|                     "delegates": [ | ||||
| @@ -129,7 +129,7 @@ define([ | ||||
|                         { | ||||
|                             "name": "Period", | ||||
|                             "control": "textfield", | ||||
|                             "cssclass": "l-small l-numeric", | ||||
|                             "cssclass": "l-input-sm l-numeric", | ||||
|                             "key": "period", | ||||
|                             "required": true, | ||||
|                             "property": [ | ||||
|   | ||||
| @@ -59,11 +59,14 @@ define( | ||||
|             update(); | ||||
|  | ||||
|             return { | ||||
|                 getGlyph: function () { | ||||
|                     return "."; | ||||
|                 }, | ||||
|                 getGlyphClass: function () { | ||||
|                     return undefined; | ||||
|                 /** | ||||
|                  * Get the CSS class that defines the icon | ||||
|                  * to display in this indicator. This will appear | ||||
|                  * as a dataflow icon. | ||||
|                  * @returns {string} the cssclass of the dataflow icon | ||||
|                  */ | ||||
|                 getCssClass: function () { | ||||
|                     return "icon-connectivity"; | ||||
|                 }, | ||||
|                 getText: function () { | ||||
|                     return displayed + " digests/sec"; | ||||
|   | ||||
| @@ -55,24 +55,13 @@ define( | ||||
|  | ||||
|             return { | ||||
|                 /** | ||||
|                  * Get the glyph (single character used as an icon) | ||||
|                  * Get the CSS class (single character used as an icon) | ||||
|                  * to display in this indicator. This will return ".", | ||||
|                  * which should appear as a dataflow icon. | ||||
|                  * which should appear as a database icon. | ||||
|                  * @returns {string} the character of the database icon | ||||
|                  */ | ||||
|                 getGlyph: function () { | ||||
|                     return "E"; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Get the name of the CSS class to apply to the glyph. | ||||
|                  * This is used to color the glyph to match its | ||||
|                  * state (one of ok, caution or err) | ||||
|                  * @returns {string} the CSS class to apply to this glyph | ||||
|                  */ | ||||
|                 getGlyphClass: function () { | ||||
|                     return (watches > 2000) ? "caution" : | ||||
|                             (watches < 1000) ? "ok" : | ||||
|                                     undefined; | ||||
|                 getCssClass: function () { | ||||
|                     return "icon-database"; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Get the text that should appear in the indicator. | ||||
|   | ||||
| @@ -50,15 +50,12 @@ define( | ||||
|             requestNext(); | ||||
|  | ||||
|             return { | ||||
|                 getGlyph: function () { | ||||
|                     return "?"; | ||||
|                 getCssClass: function () { | ||||
|                     return "icon-object-unknown"; | ||||
|                 }, | ||||
|                 getText: function () { | ||||
|                     return latest; | ||||
|                 }, | ||||
|                 getGlyphClass: function () { | ||||
|                     return ""; | ||||
|                 }, | ||||
|                 getDescription: function () { | ||||
|                     return ""; | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										62
									
								
								gulpfile.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								gulpfile.js
									
									
									
									
									
								
							| @@ -21,40 +21,34 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /*global require,__dirname*/ | ||||
|  | ||||
| var gulp = require('gulp'), | ||||
|     requirejsOptimize = require('gulp-requirejs-optimize'), | ||||
|     sourcemaps = require('gulp-sourcemaps'), | ||||
|     rename = require('gulp-rename'), | ||||
|     sass = require('gulp-sass'), | ||||
|     bourbon = require('node-bourbon'), | ||||
|     jshint = require('gulp-jshint'), | ||||
|     jscs = require('gulp-jscs'), | ||||
|     replace = require('gulp-replace-task'), | ||||
|     karma = require('karma'), | ||||
|     path = require('path'), | ||||
|     fs = require('fs'), | ||||
|     git = require('git-rev-sync'), | ||||
|     moment = require('moment'), | ||||
|     merge = require('merge-stream'), | ||||
|     project = require('./package.json'), | ||||
|     _ = require('lodash'), | ||||
|     paths = { | ||||
|         main: 'main.js', | ||||
|         main: 'openmct.js', | ||||
|         dist: 'dist', | ||||
|         assets: 'dist/assets', | ||||
|         reports: 'dist/reports', | ||||
|         scss: ['./platform/**/*.scss', './example/**/*.scss'], | ||||
|         scripts: [ 'main.js', 'platform/**/*.js', 'src/**/*.js' ], | ||||
|         assets: [ | ||||
|             './{example,platform}/**/*.{css,css.map,png,svg,ico,woff,eot,ttf}' | ||||
|         ], | ||||
|         scripts: [ 'openmct.js', 'platform/**/*.js', 'src/**/*.js' ], | ||||
|         specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ], | ||||
|         static: [ | ||||
|             'index.html', | ||||
|             'platform/**/*', | ||||
|             'example/**/*', | ||||
|             'bower_components/**/*' | ||||
|         ] | ||||
|     }, | ||||
|     options = { | ||||
|         requirejsOptimize: { | ||||
|             name: paths.main.replace(/\.js$/, ''), | ||||
|             name: 'bower_components/almond/almond.js', | ||||
|             include: paths.main.replace('.js', ''), | ||||
|             wrap: { | ||||
|                 startFile: "src/start.frag", | ||||
|                 endFile: "src/end.frag" | ||||
|             }, | ||||
|             mainConfigFile: paths.main, | ||||
|             wrapShim: true | ||||
|         }, | ||||
| @@ -63,7 +57,6 @@ var gulp = require('gulp'), | ||||
|             singleRun: true | ||||
|         }, | ||||
|         sass: { | ||||
|             includePaths: bourbon.includePaths, | ||||
|             sourceComments: true | ||||
|         }, | ||||
|         replace: { | ||||
| @@ -77,6 +70,8 @@ var gulp = require('gulp'), | ||||
|     }; | ||||
|  | ||||
| gulp.task('scripts', function () { | ||||
|     var requirejsOptimize = require('gulp-requirejs-optimize'); | ||||
|     var replace = require('gulp-replace-task'); | ||||
|     return gulp.src(paths.main) | ||||
|         .pipe(sourcemaps.init()) | ||||
|         .pipe(requirejsOptimize(options.requirejsOptimize)) | ||||
| @@ -86,10 +81,16 @@ gulp.task('scripts', function () { | ||||
| }); | ||||
|  | ||||
| gulp.task('test', function (done) { | ||||
|     var karma = require('karma'); | ||||
|     new karma.Server(options.karma, done).start(); | ||||
| }); | ||||
|  | ||||
| gulp.task('stylesheets', function () { | ||||
|     var sass = require('gulp-sass'); | ||||
|     var rename = require('gulp-rename'); | ||||
|     var bourbon = require('node-bourbon'); | ||||
|     options.sass.includePaths = bourbon.includePaths; | ||||
|  | ||||
|     return gulp.src(paths.scss, {base: '.'}) | ||||
|         .pipe(sourcemaps.init()) | ||||
|         .pipe(sass(options.sass).on('error', sass.logError)) | ||||
| @@ -103,6 +104,9 @@ gulp.task('stylesheets', function () { | ||||
| }); | ||||
|  | ||||
| gulp.task('lint', function () { | ||||
|     var jshint = require('gulp-jshint'); | ||||
|     var merge = require('merge-stream'); | ||||
|  | ||||
|     var nonspecs = paths.specs.map(function (glob) { | ||||
|             return "!" + glob; | ||||
|         }), | ||||
| @@ -112,11 +116,17 @@ gulp.task('lint', function () { | ||||
|             .pipe(jshint({ jasmine: true })); | ||||
|  | ||||
|     return merge(scriptLint, specLint) | ||||
|         .pipe(jshint.reporter('gulp-jshint-html-reporter', { | ||||
|             filename: paths.reports + '/lint/jshint-report.html', | ||||
|             createMissingFolders : true | ||||
|         })) | ||||
|         .pipe(jshint.reporter('default')) | ||||
|         .pipe(jshint.reporter('fail')); | ||||
| }); | ||||
|  | ||||
| gulp.task('checkstyle', function () { | ||||
|     var jscs = require('gulp-jscs'); | ||||
|  | ||||
|     return gulp.src(paths.scripts) | ||||
|         .pipe(jscs()) | ||||
|         .pipe(jscs.reporter()) | ||||
| @@ -124,18 +134,20 @@ gulp.task('checkstyle', function () { | ||||
| }); | ||||
|  | ||||
| gulp.task('fixstyle', function () { | ||||
|     var jscs = require('gulp-jscs'); | ||||
|  | ||||
|     return gulp.src(paths.scripts, { base: '.' }) | ||||
|         .pipe(jscs({ fix: true })) | ||||
|         .pipe(gulp.dest('.')); | ||||
| }); | ||||
|  | ||||
| gulp.task('static', ['stylesheets'], function () { | ||||
|     return gulp.src(paths.static, { base: '.' }) | ||||
| gulp.task('assets', ['stylesheets'], function () { | ||||
|     return gulp.src(paths.assets) | ||||
|         .pipe(gulp.dest(paths.dist)); | ||||
| }); | ||||
|  | ||||
| gulp.task('watch', function () { | ||||
|     gulp.watch(paths.scss, ['stylesheets']); | ||||
|     return gulp.watch(paths.scss, ['stylesheets', 'assets']); | ||||
| }); | ||||
|  | ||||
| gulp.task('serve', function () { | ||||
| @@ -143,9 +155,9 @@ gulp.task('serve', function () { | ||||
|     var app = require('./app.js'); | ||||
| }); | ||||
|  | ||||
| gulp.task('develop', ['serve', 'stylesheets', 'watch']); | ||||
| gulp.task('develop', ['serve', 'install', 'watch']); | ||||
|  | ||||
| gulp.task('install', [ 'static', 'scripts' ]); | ||||
| gulp.task('install', [ 'assets', 'scripts' ]); | ||||
|  | ||||
| gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]); | ||||
|  | ||||
|   | ||||
							
								
								
									
										39
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								index.html
									
									
									
									
									
								
							| @@ -19,36 +19,37 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head lang="en"> | ||||
|     <meta charset="UTF-8"> | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | ||||
|     <title></title> | ||||
|     <script type="text/javascript" | ||||
|             src="bower_components/requirejs/require.js"> | ||||
|     <script src="bower_components/requirejs/require.js"> | ||||
|     </script> | ||||
|     <script type="text/javascript"> | ||||
|         require(['main'], function (mct) { | ||||
|             require([ | ||||
|                 './example/imagery/bundle', | ||||
|                 './example/eventGenerator/bundle', | ||||
|                 './example/generator/bundle' | ||||
|             ], mct.run.bind(mct)); | ||||
|     <script> | ||||
|         require(['openmct'], function (openmct) { | ||||
|             [ | ||||
|                 'example/imagery', | ||||
|                 'example/eventGenerator', | ||||
|                 'example/generator', | ||||
|                 'platform/features/my-items' | ||||
|             ].forEach( | ||||
|                 openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) | ||||
|             ); | ||||
|             openmct.start(); | ||||
|         }); | ||||
|     </script> | ||||
|     <link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css"> | ||||
|     <link rel="stylesheet" href="platform/commonUI/general/res/css/openmct.css"> | ||||
| 	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32"> | ||||
| 	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96"> | ||||
| 	<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16"> | ||||
| 	<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico"> | ||||
|     <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32"> | ||||
|     <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96"> | ||||
|     <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16"> | ||||
|     <link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico"> | ||||
| </head> | ||||
| <body class="user-environ"> | ||||
|     <div class="l-splash-holder s-splash-holder"> | ||||
|         <div class="l-splash s-splash"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div ng-view></div> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| { | ||||
|     "source": { | ||||
|         "include": [ | ||||
|             "platform/" | ||||
|             "src/" | ||||
|         ], | ||||
|         "includePattern": "platform/.+\\.js$", | ||||
|         "includePattern": "src/.+\\.js$", | ||||
|         "excludePattern": ".+\\Spec\\.js$|lib/.+" | ||||
|     }, | ||||
|     "plugins": [ | ||||
|   | ||||
| @@ -37,9 +37,11 @@ module.exports = function(config) { | ||||
|             {pattern: 'bower_components/**/*.js', included: false}, | ||||
|             {pattern: 'src/**/*.js', included: false}, | ||||
|             {pattern: 'example/**/*.js', included: false}, | ||||
|             {pattern: 'example/**/*.json', included: false}, | ||||
|             {pattern: 'platform/**/*.js', included: false}, | ||||
|             {pattern: 'warp/**/*.js', included: false}, | ||||
|             {pattern: 'platform/**/*.html', included: false}, | ||||
|             {pattern: 'src/**/*.html', included: false}, | ||||
|             'test-main.js' | ||||
|         ], | ||||
|  | ||||
| @@ -81,7 +83,7 @@ module.exports = function(config) { | ||||
|         coverageReporter: { | ||||
|             dir: process.env.CIRCLE_ARTIFACTS ? | ||||
|                 process.env.CIRCLE_ARTIFACTS + '/coverage' : | ||||
|                 "dist/coverage", | ||||
|                 "dist/reports/coverage", | ||||
|             check: { | ||||
|                 global: { | ||||
|                     lines: 80 | ||||
| @@ -91,13 +93,13 @@ module.exports = function(config) { | ||||
|  | ||||
|         // HTML test reporting. | ||||
|         htmlReporter: { | ||||
|             outputDir: "target/tests", | ||||
|             outputDir: "dist/reports/tests", | ||||
|             preserveDescribeNesting: true, | ||||
|             foldAll: false | ||||
|         }, | ||||
|  | ||||
|         junitReporter: { | ||||
|             outputDir: process.env.CIRCLE_TEST_REPORTS || 'target/junit' | ||||
|             outputDir: process.env.CIRCLE_TEST_REPORTS || 'dist/reports/junit' | ||||
|         }, | ||||
|  | ||||
|         // Continuous Integration mode. | ||||
|   | ||||
| @@ -27,14 +27,18 @@ requirejs.config({ | ||||
|         "angular": "bower_components/angular/angular.min", | ||||
|         "angular-route": "bower_components/angular-route/angular-route.min", | ||||
|         "csv": "bower_components/comma-separated-values/csv.min", | ||||
|         "es6-promise": "bower_components/es6-promise/promise.min", | ||||
|         "EventEmitter": "bower_components/eventemitter3/index", | ||||
|         "es6-promise": "bower_components/es6-promise/es6-promise.min", | ||||
|         "html2canvas": "bower_components/html2canvas/build/html2canvas.min", | ||||
|         "moment": "bower_components/moment/moment", | ||||
|         "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", | ||||
|         "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", | ||||
|         "zepto": "bower_components/zepto/zepto.min" | ||||
|         "zepto": "bower_components/zepto/zepto.min", | ||||
|         "lodash": "bower_components/lodash/lodash", | ||||
|         "d3": "bower_components/d3/d3.min" | ||||
|     }, | ||||
|     "shim": { | ||||
|         "angular": { | ||||
| @@ -43,6 +47,12 @@ requirejs.config({ | ||||
|         "angular-route": { | ||||
|             "deps": ["angular"] | ||||
|         }, | ||||
|         "EventEmitter": { | ||||
|             "exports": "EventEmitter" | ||||
|         }, | ||||
|         "html2canvas": { | ||||
|             "exports": "html2canvas" | ||||
|         }, | ||||
|         "moment-duration-format": { | ||||
|             "deps": ["moment"] | ||||
|         }, | ||||
| @@ -51,54 +61,28 @@ requirejs.config({ | ||||
|         }, | ||||
|         "zepto": { | ||||
|             "exports": "Zepto" | ||||
|         }, | ||||
|         "lodash": { | ||||
|             "exports": "lodash" | ||||
|         }, | ||||
|         "d3": { | ||||
|             "exports": "d3" | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| define([ | ||||
|     './platform/framework/src/Main', | ||||
|     'legacyRegistry', | ||||
|     './src/defaultRegistry', | ||||
|     './src/MCT' | ||||
| ], function (Main, defaultRegistry, MCT) { | ||||
|     var openmct = new MCT(); | ||||
| 
 | ||||
|     './platform/framework/bundle', | ||||
|     './platform/core/bundle', | ||||
|     './platform/representation/bundle', | ||||
|     './platform/commonUI/about/bundle', | ||||
|     './platform/commonUI/browse/bundle', | ||||
|     './platform/commonUI/edit/bundle', | ||||
|     './platform/commonUI/dialog/bundle', | ||||
|     './platform/commonUI/formats/bundle', | ||||
|     './platform/commonUI/general/bundle', | ||||
|     './platform/commonUI/inspect/bundle', | ||||
|     './platform/commonUI/mobile/bundle', | ||||
|     './platform/commonUI/themes/espresso/bundle', | ||||
|     './platform/commonUI/notification/bundle', | ||||
|     './platform/containment/bundle', | ||||
|     './platform/execution/bundle', | ||||
|     './platform/exporters/bundle', | ||||
|     './platform/telemetry/bundle', | ||||
|     './platform/features/clock/bundle', | ||||
|     './platform/features/fixed/bundle', | ||||
|     './platform/features/imagery/bundle', | ||||
|     './platform/features/layout/bundle', | ||||
|     './platform/features/pages/bundle', | ||||
|     './platform/features/plot/bundle', | ||||
|     './platform/features/timeline/bundle', | ||||
|     './platform/features/table/bundle', | ||||
|     './platform/forms/bundle', | ||||
|     './platform/identity/bundle', | ||||
|     './platform/persistence/aggregator/bundle', | ||||
|     './platform/persistence/local/bundle', | ||||
|     './platform/persistence/queue/bundle', | ||||
|     './platform/policy/bundle', | ||||
|     './platform/entanglement/bundle', | ||||
|     './platform/search/bundle', | ||||
|     './platform/status/bundle', | ||||
|     './platform/commonUI/regions/bundle' | ||||
| ], function (Main, legacyRegistry) { | ||||
|     return { | ||||
|         legacyRegistry: legacyRegistry, | ||||
|         run: function () { | ||||
|             return new Main().run(legacyRegistry); | ||||
|         } | ||||
|     }; | ||||
|     openmct.legacyRegistry = defaultRegistry; | ||||
| 
 | ||||
|     openmct.on('start', function () { | ||||
|         return new Main().run(defaultRegistry); | ||||
|     }); | ||||
| 
 | ||||
|     return openmct; | ||||
| }); | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "openmct", | ||||
|   "version": "0.11.0", | ||||
|   "version": "0.12.0-SNAPSHOT", | ||||
|   "description": "The Open MCT core platform", | ||||
|   "dependencies": { | ||||
|     "express": "^4.13.1", | ||||
| @@ -14,6 +14,7 @@ | ||||
|     "gulp": "^3.9.0", | ||||
|     "gulp-jscs": "^3.0.2", | ||||
|     "gulp-jshint": "^2.0.0", | ||||
|     "gulp-jshint-html-reporter": "^0.1.3", | ||||
|     "gulp-rename": "^1.2.2", | ||||
|     "gulp-replace-task": "^0.11.0", | ||||
|     "gulp-requirejs-optimize": "^0.3.1", | ||||
| @@ -38,7 +39,7 @@ | ||||
|     "mkdirp": "^0.5.1", | ||||
|     "moment": "^2.11.1", | ||||
|     "node-bourbon": "^4.2.3", | ||||
|     "phantomjs-prebuilt": "^2.1.0", | ||||
|     "phantomjs-prebuilt": "2.1.11 || >2.1.12 <3.0.0", | ||||
|     "requirejs": "2.1.x", | ||||
|     "split": "^1.0.0" | ||||
|   }, | ||||
| @@ -47,8 +48,8 @@ | ||||
|     "test": "karma start --single-run", | ||||
|     "jshint": "jshint platform example", | ||||
|     "watch": "karma start", | ||||
|     "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'", | ||||
|     "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api", | ||||
|     "otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", | ||||
|     "docs": "npm run jsdoc ; npm run otherdoc", | ||||
|     "prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install" | ||||
|   }, | ||||
|   | ||||
| @@ -41,6 +41,7 @@ define([ | ||||
|     "text!./res/templates/items/items.html", | ||||
|     "text!./res/templates/browse/object-properties.html", | ||||
|     "text!./res/templates/browse/inspector-region.html", | ||||
|     "text!./res/templates/view-object.html", | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     BrowseController, | ||||
| @@ -63,6 +64,7 @@ define([ | ||||
|     itemsTemplate, | ||||
|     objectPropertiesTemplate, | ||||
|     inspectorRegionTemplate, | ||||
|     viewObjectTemplate, | ||||
|     legacyRegistry | ||||
| ) { | ||||
|  | ||||
| @@ -142,7 +144,7 @@ define([ | ||||
|             "representations": [ | ||||
|                 { | ||||
|                     "key": "view-object", | ||||
|                     "templateUrl": "templates/view-object.html" | ||||
|                     "template": viewObjectTemplate | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "browse-object", | ||||
| @@ -231,7 +233,7 @@ define([ | ||||
|                         "$window" | ||||
|                     ], | ||||
|                     "group": "windowing", | ||||
|                     "glyph": "y", | ||||
|                     "cssclass": "icon-new-window", | ||||
|                     "priority": "preferred" | ||||
|                 }, | ||||
|                 { | ||||
| @@ -239,7 +241,6 @@ define([ | ||||
|                     "implementation": FullscreenAction, | ||||
|                     "category": "view-control", | ||||
|                     "group": "windowing", | ||||
|                     "glyph": "z", | ||||
|                     "priority": "default" | ||||
|                 } | ||||
|             ], | ||||
| @@ -247,7 +248,7 @@ define([ | ||||
|                 { | ||||
|                     "key": "items", | ||||
|                     "name": "Items", | ||||
|                     "glyph": "9", | ||||
|                     "cssclass": "icon-thumbs-strip", | ||||
|                     "description": "Grid of available items", | ||||
|                     "template": itemsTemplate, | ||||
|                     "uses": [ | ||||
|   | ||||
| @@ -20,11 +20,8 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
|  | ||||
| <!-- Back Arrow Icon used on mobile--> | ||||
| <a | ||||
|    class='type-icon icon ui-symbol s-back' | ||||
| <a class='s-icon-button icon-pointer-left' | ||||
|    ng-show="context.getPath().length > 2" | ||||
|    ng-click="context.getParent().getCapability('action').perform('navigate')"> | ||||
|     { | ||||
| </a> | ||||
|  | ||||
|   | ||||
| @@ -43,7 +43,7 @@ | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="holder l-flex-col flex-elem grows l-object-wrapper"> | ||||
|     <div class="holder l-flex-col flex-elem grows l-object-wrapper l-controls-visible l-time-controller-visible"> | ||||
|         <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"> | ||||
| @@ -59,4 +59,5 @@ | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|     </div> | ||||
|     <mct-include key="'conductor'" class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"></mct-include> | ||||
| </div> | ||||
|   | ||||
| @@ -31,7 +31,7 @@ | ||||
|                 <div class="abs holder l-flex-col holder-treeview-elements"> | ||||
|                     <mct-representation key="'create-button'" | ||||
|                                         mct-object="navigatedObject" | ||||
|                                         class="holder flex-elem create-btn-holder"> | ||||
|                                         class="holder flex-elem create-button-holder"> | ||||
|                     </mct-representation> | ||||
|                     <mct-include key="'search'" | ||||
|                                  ng-model="treeModel" | ||||
| @@ -66,6 +66,10 @@ | ||||
|                                                 key="'view-object'" | ||||
|                                                 class="abs holder holder-object"> | ||||
|                             </mct-representation> | ||||
|                             <a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right" | ||||
|                                title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane" | ||||
|                                ng-click="modelPaneInspect.toggle()" | ||||
|                                ng-class="{ collapsed : !modelPaneInspect.visible() }"></a> | ||||
|                         </div> | ||||
|  | ||||
|                         <mct-splitter class="splitter-inspect mobile-hide flush-right edge-shdw"></mct-splitter> | ||||
| @@ -75,10 +79,6 @@ | ||||
|                                                 mct-object="navigatedObject" | ||||
|                                                 ng-model="treeModel"> | ||||
|                             </mct-representation> | ||||
|                             <a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect" | ||||
|                                title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane" | ||||
|                                ng-click="modelPaneInspect.toggle()" | ||||
|                                ng-class="{ collapsed : !modelPaneInspect.visible() }"></a> | ||||
|                         </div> | ||||
|                     </mct-split-pane> | ||||
|                 </div> | ||||
|   | ||||
| @@ -19,10 +19,11 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span class='type-icon ui-symbol flex-elem'>{{type.getGlyph()}}</span> | ||||
| <span class='type-icon flex-elem {{type.getCssClass()}}'></span> | ||||
| <span class="l-elem-wrapper l-flex-row flex-elem grows"> | ||||
|     <span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span> | ||||
|     <span class='title-label flex-elem flex-can-shrink'>{{model.name}}</span> | ||||
|     <span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span> | ||||
|     <span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span> | ||||
|     <mct-representation | ||||
|         key="'menu-arrow'" | ||||
|         mct-object='domainObject' | ||||
|   | ||||
| @@ -23,14 +23,14 @@ | ||||
| <div class='item grid-item' ng-click='action.perform("navigate")'> | ||||
|     <div class='contents abs'> | ||||
|         <div class='top-bar bar abs'> | ||||
|             <div class='ui-symbol profile' title='Shared'>O</div> | ||||
|             <span class='icon-people' title='Shared'></span> | ||||
|             <mct-representation class="desktop-hide" key="'info-button'" mct-object="domainObject"></mct-representation> | ||||
|         </div> | ||||
|         <div class='item-main abs lg'> | ||||
|             <span class="t-item-icon" ng-class="{ 'l-icon-link':location.isLink() }"> | ||||
|                 <span class="t-item-icon-glyph ng-binding">{{type.getGlyph()}}</span> | ||||
|                 <span class="t-item-icon-glyph ng-binding {{type.getCssClass()}}"></span> | ||||
|             </span> | ||||
|             <div class='ui-symbol abs item-open'>}</div> | ||||
|             <div class='abs item-open icon-pointer-right'></div> | ||||
|         </div> | ||||
|         <div class='bottom-bar bar abs'> | ||||
|             <div class='title'>{{model.name}}</div> | ||||
|   | ||||
| @@ -21,6 +21,6 @@ | ||||
| --> | ||||
|  | ||||
| <span ng-controller="MenuArrowController as menuArrow"> | ||||
|     <a class='ui-symbol context-available' | ||||
|        ng-click='menuArrow.showMenu($event)'>v</a> | ||||
|     <a class='context-available' | ||||
|        ng-click='menuArrow.showMenu($event)'></a> | ||||
| </span> | ||||
| @@ -48,20 +48,35 @@ define( | ||||
|          */ | ||||
|         NavigateAction.prototype.perform = function () { | ||||
|             var self = this, | ||||
|                 navigationAllowed = true; | ||||
|                 navigateTo = this.domainObject, | ||||
|                 currentObject = self.navigationService.getNavigation(); | ||||
|  | ||||
|             function allow() { | ||||
|                 self.policyService.allow("navigation", self.navigationService.getNavigation(), self.domainObject, function (message) { | ||||
|                 var navigationAllowed = true; | ||||
|                 self.policyService.allow("navigation", currentObject, navigateTo, function (message) { | ||||
|                     navigationAllowed = self.$window.confirm(message + "\r\n\r\n" + | ||||
|                         " Are you sure you want to continue?"); | ||||
|                 }); | ||||
|                 return navigationAllowed; | ||||
|             } | ||||
|  | ||||
|             // Set navigation, and wrap like a promise | ||||
|             return this.$q.when( | ||||
|                 allow() && this.navigationService.setNavigation(this.domainObject) | ||||
|             ); | ||||
|             function cancelIfEditing() { | ||||
|                 var editing = currentObject.hasCapability('editor') && | ||||
|                     currentObject.getCapability('editor').isEditContextRoot(); | ||||
|  | ||||
|                 return self.$q.when(editing && currentObject.getCapability("editor").finish()); | ||||
|             } | ||||
|  | ||||
|             function navigate() { | ||||
|                 return self.navigationService.setNavigation(navigateTo); | ||||
|             } | ||||
|  | ||||
|             if (allow()) { | ||||
|                 return cancelIfEditing().then(navigate); | ||||
|             } else { | ||||
|                 return this.$q.when(false); | ||||
|             } | ||||
|  | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -46,12 +46,12 @@ define( | ||||
|         }; | ||||
|  | ||||
|         FullscreenAction.prototype.getMetadata = function () { | ||||
|             // We override getMetadata, because the glyph and | ||||
|             // We override getMetadata, because the icon cssclass and | ||||
|             // description need to be determined at run-time | ||||
|             // based on whether or not we are currently | ||||
|             // full screen. | ||||
|             var metadata = Object.create(FullscreenAction); | ||||
|             metadata.glyph = screenfull.isFullscreen ? "_" : "z"; | ||||
|             metadata.cssclass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse"; | ||||
|             metadata.description = screenfull.isFullscreen ? | ||||
|                 EXIT_FULLSCREEN : ENTER_FULLSCREEN; | ||||
|             metadata.group = "windowing"; | ||||
|   | ||||
| @@ -32,7 +32,9 @@ define( | ||||
|                 mockQ, | ||||
|                 mockDomainObject, | ||||
|                 mockPolicyService, | ||||
|                 mockNavigatedObject, | ||||
|                 mockWindow, | ||||
|                 capabilities, | ||||
|                 action; | ||||
|  | ||||
|             function mockPromise(value) { | ||||
| @@ -44,6 +46,29 @@ define( | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 capabilities = {}; | ||||
|  | ||||
|                 mockQ = { when: mockPromise }; | ||||
|                 mockNavigatedObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ | ||||
|                         "getId", | ||||
|                         "getModel", | ||||
|                         "hasCapability", | ||||
|                         "getCapability" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 capabilities.editor = jasmine.createSpyObj("editorCapability", [ | ||||
|                     "isEditContextRoot", | ||||
|                     "finish" | ||||
|                 ]); | ||||
|  | ||||
|                 mockNavigatedObject.getCapability.andCallFake(function (capability) { | ||||
|                     return capabilities[capability]; | ||||
|                 }); | ||||
|                 mockNavigatedObject.hasCapability.andReturn(false); | ||||
|  | ||||
|                 mockNavigationService = jasmine.createSpyObj( | ||||
|                     "navigationService", | ||||
|                     [ | ||||
| @@ -51,11 +76,14 @@ define( | ||||
|                         "getNavigation" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockNavigationService.getNavigation.andReturn({}); | ||||
|                 mockQ = { when: mockPromise }; | ||||
|                 mockNavigationService.getNavigation.andReturn(mockNavigatedObject); | ||||
|  | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     ["getId", "getModel", "getCapability"] | ||||
|                     [ | ||||
|                         "getId", | ||||
|                         "getModel" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 mockPolicyService = jasmine.createSpyObj("policyService", | ||||
| @@ -112,6 +140,21 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("in edit mode", function () { | ||||
|                 beforeEach(function () { | ||||
|                     mockNavigatedObject.hasCapability.andCallFake(function (capability) { | ||||
|                         return capability === "editor"; | ||||
|                     }); | ||||
|                     capabilities.editor.isEditContextRoot.andReturn(true); | ||||
|                 }); | ||||
|  | ||||
|                 it("finishes editing if in edit mode", function () { | ||||
|                     action.perform(); | ||||
|                     expect(capabilities.editor.finish) | ||||
|                         .toHaveBeenCalled(); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("is only applicable when a domain object is in context", function () { | ||||
|                 expect(NavigateAction.appliesTo({})).toBeFalsy(); | ||||
|                 expect(NavigateAction.appliesTo({ | ||||
|   | ||||
| @@ -51,7 +51,7 @@ define( | ||||
|             }); | ||||
|  | ||||
|             it("provides displayable metadata", function () { | ||||
|                 expect(action.getMetadata().glyph).toBeDefined(); | ||||
|                 expect(action.getMetadata().cssclass).toBeDefined(); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|   | ||||
| @@ -21,21 +21,22 @@ | ||||
| --> | ||||
| <div class="abs top-bar"> | ||||
|     <div class="title">{{ngModel.title}}</div> | ||||
|     <div class="hint">All fields marked <span class="ui-symbol req">*</span> are required.</div> | ||||
|     <div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div> | ||||
| </div> | ||||
| <div class='abs editor'> | ||||
|     <mct-form ng-model="ngModel.value" | ||||
|               structure="ngModel.structure" | ||||
|               class="validates" | ||||
|               name="createForm"> | ||||
|     </mct-form> | ||||
| </div> | ||||
| <div class="abs bottom-bar"> | ||||
|     <a class='s-btn major' | ||||
|     <a class='s-button major' | ||||
|        ng-class="{ disabled: !createForm.$valid }" | ||||
|        ng-click="ngModel.confirm()"> | ||||
|         OK | ||||
|     </a> | ||||
|     <a class='s-btn' | ||||
|     <a class='s-button' | ||||
|        ng-click="ngModel.cancel()"> | ||||
|         Cancel | ||||
|     </a> | ||||
|   | ||||
| @@ -16,11 +16,11 @@ | ||||
|         </div> | ||||
|         <div class="bottom-bar"> | ||||
|             <a ng-repeat="dialogOption in ngModel.options" | ||||
|                class="s-btn major" | ||||
|                class="s-button major" | ||||
|                ng-click="dialogOption.callback()"> | ||||
|                 {{dialogOption.label}} | ||||
|             </a> | ||||
|             <a class="s-btn major" | ||||
|             <a class="s-button major" | ||||
|                ng-if="ngModel.primaryOption" | ||||
|                ng-click="ngModel.primaryOption.callback()"> | ||||
|                 {{ngModel.primaryOption.label}} | ||||
|   | ||||
| @@ -2,7 +2,8 @@ | ||||
|     <div class="message-contents"> | ||||
|         <div class="abs top-bar"> | ||||
|             <div class="title">{{ngModel.dialog.title}}</div> | ||||
|             <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1">s</span> | ||||
|             <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 || | ||||
|                                                                                                   ngModel.dialog.messages.length == 0">s</span> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="abs message-body"> | ||||
| @@ -12,7 +13,7 @@ | ||||
|         </div> | ||||
|         <div class="abs bottom-bar"> | ||||
|             <a ng-repeat="dialogAction in ngModel.dialog.actions" | ||||
|                class="s-btn major" | ||||
|                class="s-button major" | ||||
|                ng-click="dialogAction.action()"> | ||||
|                 {{dialogAction.label}} | ||||
|             </a> | ||||
|   | ||||
| @@ -33,7 +33,7 @@ | ||||
|     <div class="abs bottom-bar"> | ||||
|         <a ng-repeat="option in ngModel.dialog.options" | ||||
|            href='' | ||||
|            class="s-btn lg" | ||||
|            class="s-button lg" | ||||
|            title="{{option.description}}" | ||||
|            ng-click="ngModel.confirm(option.key)" | ||||
|            ng-class="{ major: $first, subtle: !$first }"> | ||||
|   | ||||
| @@ -19,12 +19,12 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="abs overlay"> | ||||
| <div class="abs overlay" ng-class="{'delayEntry100ms' : ngModel.delay}"> | ||||
|     <div class="abs blocker"></div> | ||||
|     <div class="abs holder"> | ||||
|         <a ng-click="ngModel.cancel()" | ||||
|            ng-if="ngModel.cancel" | ||||
|            class="clk-icon icon ui-symbol close">x</a> | ||||
|            class="close icon-x"></a> | ||||
|         <div class="abs contents" ng-transclude></div> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -187,7 +187,7 @@ define( | ||||
|  | ||||
|         /** | ||||
|          * A description of the model options that may be passed to the | ||||
|          * showBlockingMessage method. Note that the DialogModel desribed | ||||
|          * showBlockingMessage method. Note that the DialogModel described | ||||
|          * here is shared with the Notifications framework. | ||||
|          * @see NotificationService | ||||
|          * | ||||
| @@ -200,6 +200,9 @@ define( | ||||
|          * shown above a progress bar to indicate what's happening. | ||||
|          * @property {number} progress a percentage value (1-100) | ||||
|          * indicating the completion of the blocking task | ||||
|          * @property {boolean} delay adds a brief delay before loading | ||||
|          * the dialog. Useful for removing the dialog flicker when the | ||||
|          * conditions for displaying the dialog change rapidly. | ||||
|          * @property {string} progressText the message to show below a | ||||
|          * progress bar to indicate progress. For example, this might be | ||||
|          * used to indicate time remaining, or items still to process. | ||||
|   | ||||
| @@ -31,6 +31,7 @@ define([ | ||||
|     "./src/actions/PropertiesAction", | ||||
|     "./src/actions/RemoveAction", | ||||
|     "./src/actions/SaveAction", | ||||
|     "./src/actions/SaveAndStopEditingAction", | ||||
|     "./src/actions/SaveAsAction", | ||||
|     "./src/actions/CancelAction", | ||||
|     "./src/policies/EditActionPolicy", | ||||
| @@ -70,6 +71,7 @@ define([ | ||||
|     PropertiesAction, | ||||
|     RemoveAction, | ||||
|     SaveAction, | ||||
|     SaveAndStopEditingAction, | ||||
|     SaveAsAction, | ||||
|     CancelAction, | ||||
|     EditActionPolicy, | ||||
| @@ -174,7 +176,7 @@ define([ | ||||
|                     ], | ||||
|                     "description": "Edit", | ||||
|                     "category": "view-control", | ||||
|                     "glyph": "p" | ||||
|                     "cssclass": "major icon-pencil" | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "properties", | ||||
| @@ -183,7 +185,7 @@ define([ | ||||
|                         "view-control" | ||||
|                     ], | ||||
|                     "implementation": PropertiesAction, | ||||
|                     "glyph": "p", | ||||
|                     "cssclass": "major icon-pencil", | ||||
|                     "name": "Edit Properties...", | ||||
|                     "description": "Edit properties of this object.", | ||||
|                     "depends": [ | ||||
| @@ -194,36 +196,46 @@ define([ | ||||
|                     "key": "remove", | ||||
|                     "category": "contextual", | ||||
|                     "implementation": RemoveAction, | ||||
|                     "glyph": "Z", | ||||
|                     "cssclass": "icon-trash", | ||||
|                     "name": "Remove", | ||||
|                     "description": "Remove this object from its containing object.", | ||||
|                     "depends": [ | ||||
|                         "$q", | ||||
|                         "navigationService" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "save", | ||||
|                     "category": "conclude-editing", | ||||
|                     "implementation": SaveAction, | ||||
|                     "name": "Save", | ||||
|                     "key": "save-and-stop-editing", | ||||
|                     "category": "save", | ||||
|                     "implementation": SaveAndStopEditingAction, | ||||
|                     "name": "Save and Finish Editing", | ||||
|                     "cssclass": "icon-save labeled", | ||||
|                     "description": "Save changes made to these objects.", | ||||
|                     "depends": [ | ||||
|                         "dialogService" | ||||
|                     ], | ||||
|                     "priority": "mandatory" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "save", | ||||
|                     "category": "conclude-editing", | ||||
|                     "category": "save", | ||||
|                     "implementation": SaveAction, | ||||
|                     "name": "Save and Continue Editing", | ||||
|                     "cssclass": "icon-save labeled", | ||||
|                     "description": "Save changes made to these objects.", | ||||
|                     "depends": [ | ||||
|                         "dialogService" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "save-as", | ||||
|                     "category": "save", | ||||
|                     "implementation": SaveAsAction, | ||||
|                     "name": "Save", | ||||
|                     "name": "Save As...", | ||||
|                     "cssclass": "icon-save labeled", | ||||
|                     "description": "Save changes made to these objects.", | ||||
|                     "depends": [ | ||||
|                         "$injector", | ||||
|                         "policyService", | ||||
|                         "dialogService", | ||||
|                         "creationService", | ||||
|                         "copyService" | ||||
|                     ], | ||||
|                     "priority": "mandatory" | ||||
| @@ -232,7 +244,10 @@ define([ | ||||
|                     "key": "cancel", | ||||
|                     "category": "conclude-editing", | ||||
|                     "implementation": CancelAction, | ||||
|                     "name": "Cancel", | ||||
|                     // Because we use the name as label for edit buttons and mct-control buttons need | ||||
|                     // the label to be set to undefined in order to not apply the labeled CSS rule. | ||||
|                     "name": undefined, | ||||
|                     "cssclass": "icon-x no-label", | ||||
|                     "description": "Discard changes made to these objects.", | ||||
|                     "depends": [] | ||||
|                 } | ||||
| @@ -384,7 +399,7 @@ define([ | ||||
|             "constants": [ | ||||
|                 { | ||||
|                     "key": "editModeBlacklist", | ||||
|                     "value": ["copy", "follow", "window", "link", "locate"] | ||||
|                     "value": ["copy", "follow", "link", "locate"] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "nonEditContextBlacklist", | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span ng-controller="ClickAwayController as createController"> | ||||
|     <div class="s-menu-btn major create-btn" ng-click="createController.toggle()"> | ||||
|     <div class="s-menu-button major create-button" ng-click="createController.toggle()"> | ||||
| 		<span class="title-label">Create</span> | ||||
|     </div> | ||||
|     <div class="menu super-menu" ng-show="createController.isActive()"> | ||||
|   | ||||
| @@ -22,24 +22,17 @@ | ||||
| <div class="contents" ng-controller="CreateMenuController"> | ||||
|     <div class="pane left menu-items"> | ||||
|         <ul> | ||||
|  | ||||
|             <li ng-repeat="createAction in createActions" ng-click="createAction.perform()"> | ||||
|                 <a | ||||
|                    ng-mouseover="representation.activeMetadata = createAction.getMetadata()" | ||||
|                    ng-mouseleave="representation.activeMetadata = undefined"> | ||||
|                     <span class="ui-symbol icon type-icon"> | ||||
|                         {{createAction.getMetadata().glyph}} | ||||
|                     </span> | ||||
|                 <a ng-mouseover="representation.activeMetadata = createAction.getMetadata()" | ||||
|                    ng-mouseleave="representation.activeMetadata = undefined" | ||||
|                    class="menu-item-a {{ createAction.getMetadata().cssclass }}"> | ||||
|                     {{createAction.getMetadata().name}} | ||||
|                 </a> | ||||
|             </li> | ||||
|  | ||||
|         </ul> | ||||
|     </div> | ||||
|     <div class="pane right menu-item-description"> | ||||
|         <div class="desc-area ui-symbol icon type-icon"> | ||||
|             {{representation.activeMetadata.glyph}} | ||||
|         </div> | ||||
|         <div class="desc-area icon {{ representation.activeMetadata.cssclass }}"></div> | ||||
|         <div class="desc-area title"> | ||||
|             {{representation.activeMetadata.name}} | ||||
|         </div> | ||||
|   | ||||
| @@ -20,12 +20,34 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span ng-controller="EditActionController"> | ||||
|     <span ng-repeat="currentAction in editActions"> | ||||
|         <a class='s-btn t-{{currentAction.getMetadata().key}}' | ||||
|            title='{{currentAction.getMetadata().name}}' | ||||
|            ng-click="currentAction.perform()" | ||||
|            ng-class="{ major: $index === 0 }"> | ||||
|             <span class="title-label">{{currentAction.getMetadata().name}}</span> | ||||
|         </a> | ||||
|     <!-- If there's a single save action show a button, otherwise show a dropdown with all save actions. --> | ||||
|     <span ng-if="saveActions.length === 1"> | ||||
|         <mct-control key="'button'" | ||||
|                      structure="{ | ||||
|                         text: saveActions[0].getMetadata().name, | ||||
|                         click: actionPerformer(saveActions[0]), | ||||
|                         cssclass: 'major ' + saveActions[0].getMetadata().cssclass | ||||
|                      }"> | ||||
|         </mct-control> | ||||
|     </span> | ||||
|  | ||||
|     <span ng-if="saveActions.length > 1"> | ||||
|         <mct-control key="'menu-button'" | ||||
|                      structure="{ | ||||
|                         options: saveActionsAsMenuOptions, | ||||
|                         click: saveActionMenuClickHandler, | ||||
|                         cssclass: 'btn-bar right icon-save no-label major' | ||||
|                      }"> | ||||
|         </mct-control> | ||||
|     </span> | ||||
|  | ||||
|     <span ng-repeat="currentAction in otherEditActions"> | ||||
|         <mct-control key="'button'" | ||||
|                      structure="{ | ||||
|                         text: currentAction.getMetadata().name, | ||||
|                         click: actionPerformer(currentAction), | ||||
|                         cssclass: currentAction.getMetadata().cssclass | ||||
|                      }"> | ||||
|         </mct-control> | ||||
|     </span> | ||||
| </span> | ||||
| @@ -66,4 +66,9 @@ | ||||
|             </mct-representation> | ||||
|         </div><!--/ l-object-wrapper-inner --> | ||||
|     </div> | ||||
|     <mct-representation mct-object="domainObject" | ||||
|                         key="'time-conductor'" | ||||
|                         class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"> | ||||
|     </mct-representation> | ||||
|  | ||||
| </div> | ||||
|   | ||||
| @@ -62,7 +62,7 @@ define( | ||||
|             } | ||||
|  | ||||
|             function cancel(allowed) { | ||||
|                 return allowed && domainObject.getCapability("editor").cancel(); | ||||
|                 return allowed && domainObject.getCapability("editor").finish(); | ||||
|             } | ||||
|  | ||||
|             //Do navigation first in order to trigger unsaved changes dialog | ||||
|   | ||||
| @@ -69,18 +69,13 @@ define( | ||||
|          * Enter edit mode. | ||||
|          */ | ||||
|         EditAction.prototype.perform = function () { | ||||
|             var self = this; | ||||
|             function cancelEditing() { | ||||
|                 self.domainObject.getCapability('editor').cancel(); | ||||
|                 self.navigationService.removeListener(cancelEditing); | ||||
|             } | ||||
|  | ||||
|             //If this is not the currently navigated object, then navigate | ||||
|             // to it. | ||||
|             if (this.navigationService.getNavigation() !== this.domainObject) { | ||||
|                 this.navigationService.setNavigation(this.domainObject); | ||||
|             } | ||||
|  | ||||
|             this.navigationService.addListener(cancelEditing); | ||||
|             this.domainObject.useCapability("editor"); | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -40,19 +40,11 @@ define( | ||||
|             var self = this, | ||||
|                 editAction = this.domainObject.getCapability('action').getActions("edit")[0]; | ||||
|  | ||||
|             // Persist changes to the domain object | ||||
|             function doPersist() { | ||||
|                 var persistence = | ||||
|                     self.domainObject.getCapability('persistence'); | ||||
|                 return persistence.persist(); | ||||
|             } | ||||
|  | ||||
|             // Link these objects | ||||
|             function doLink() { | ||||
|                 var composition = self.domainObject && | ||||
|                         self.domainObject.getCapability('composition'); | ||||
|                 return composition && composition.add(self.selectedObject) | ||||
|                         .then(doPersist); | ||||
|                 return composition && composition.add(self.selectedObject); | ||||
|             } | ||||
|  | ||||
|             if (editAction) { | ||||
|   | ||||
| @@ -50,12 +50,6 @@ define( | ||||
|                 domainObject = this.domainObject, | ||||
|                 dialogService = this.dialogService; | ||||
|  | ||||
|             // Persist modifications to this domain object | ||||
|             function doPersist() { | ||||
|                 var persistence = domainObject.getCapability('persistence'); | ||||
|                 return persistence && persistence.persist(); | ||||
|             } | ||||
|  | ||||
|             // Update the domain object model based on user input | ||||
|             function updateModel(userInput, dialog) { | ||||
|                 return domainObject.useCapability('mutation', function (model) { | ||||
| @@ -73,11 +67,9 @@ define( | ||||
|                     dialog.getFormStructure(), | ||||
|                     dialog.getInitialFormValue() | ||||
|                 ).then(function (userInput) { | ||||
|                         // Update the model, if user input was provided | ||||
|                         return userInput && updateModel(userInput, dialog); | ||||
|                     }).then(function (result) { | ||||
|                         return result && doPersist(); | ||||
|                     }); | ||||
|                     // Update the model, if user input was provided | ||||
|                     return userInput && updateModel(userInput, dialog); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             return type && showDialog(type); | ||||
| @@ -94,9 +86,7 @@ define( | ||||
|                 creatable = type && type.hasFeature('creation'); | ||||
|  | ||||
|             // Only allow creatable types to be edited | ||||
|             return domainObject && | ||||
|                 domainObject.hasCapability("persistence") && | ||||
|                 creatable; | ||||
|             return domainObject && creatable; | ||||
|         }; | ||||
|  | ||||
|         return PropertiesAction; | ||||
|   | ||||
| @@ -39,9 +39,8 @@ define( | ||||
|          * @constructor | ||||
|          * @implements {Action} | ||||
|          */ | ||||
|         function RemoveAction($q, navigationService, context) { | ||||
|         function RemoveAction(navigationService, context) { | ||||
|             this.domainObject = (context || {}).domainObject; | ||||
|             this.$q = $q; | ||||
|             this.navigationService = navigationService; | ||||
|         } | ||||
|  | ||||
| @@ -51,8 +50,7 @@ define( | ||||
|          *         fulfilled when the action has completed. | ||||
|          */ | ||||
|         RemoveAction.prototype.perform = function () { | ||||
|             var $q = this.$q, | ||||
|                 navigationService = this.navigationService, | ||||
|             var navigationService = this.navigationService, | ||||
|                 domainObject = this.domainObject; | ||||
|             /* | ||||
|              * Check whether an object ID matches the ID of the object being | ||||
| @@ -71,15 +69,6 @@ define( | ||||
|                 model.composition = model.composition.filter(isNotObject); | ||||
|             } | ||||
|  | ||||
|             /* | ||||
|              * Invoke persistence on a domain object. This will be called upon | ||||
|              * the removed object's parent (as its composition will have changed.) | ||||
|              */ | ||||
|             function doPersist(domainObj) { | ||||
|                 var persistence = domainObj.getCapability('persistence'); | ||||
|                 return persistence && persistence.persist(); | ||||
|             } | ||||
|  | ||||
|             /* | ||||
|              * Checks current object and ascendants of current | ||||
|              * object with object being removed, if the current | ||||
| @@ -119,15 +108,10 @@ define( | ||||
|                 // navigates to existing object up tree | ||||
|                 checkObjectNavigation(object, parent); | ||||
|  | ||||
|                 return $q.when( | ||||
|                     parent.useCapability('mutation', doMutate) | ||||
|                 ).then(function () { | ||||
|                     return doPersist(parent); | ||||
|                 }); | ||||
|                 return parent.useCapability('mutation', doMutate); | ||||
|             } | ||||
|  | ||||
|             return $q.when(domainObject) | ||||
|                 .then(removeFromContext); | ||||
|             return removeFromContext(domainObject); | ||||
|         }; | ||||
|  | ||||
|         // Object needs to have a parent for Remove to be applicable | ||||
|   | ||||
| @@ -25,9 +25,8 @@ define( | ||||
|     function (SaveInProgressDialog) { | ||||
|  | ||||
|         /** | ||||
|          * 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. | ||||
|          * The "Save" action; it invokes object capabilities to persist | ||||
|          * the changes that have been made. | ||||
|          * @constructor | ||||
|          * @implements {Action} | ||||
|          * @memberof platform/commonUI/edit | ||||
| @@ -41,7 +40,7 @@ define( | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Save changes and conclude editing. | ||||
|          * Save changes. | ||||
|          * | ||||
|          * @returns {Promise} a promise that will be fulfilled when | ||||
|          *          cancellation has completed | ||||
| @@ -51,40 +50,22 @@ define( | ||||
|             var domainObject = this.domainObject, | ||||
|                 dialog = new SaveInProgressDialog(this.dialogService); | ||||
|  | ||||
|             function resolveWith(object) { | ||||
|                 return function () { | ||||
|                     return object; | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             // 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() { | ||||
|                 return domainObject.getCapability("editor").save() | ||||
|                     .then(resolveWith(domainObject)); | ||||
|                 return domainObject.getCapability("editor").save(); | ||||
|             } | ||||
|  | ||||
|             // 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; | ||||
|             } | ||||
|  | ||||
|             function hideBlockingDialog(object) { | ||||
|             function hideBlockingDialog() { | ||||
|                 dialog.hide(); | ||||
|                 return object; | ||||
|             } | ||||
|  | ||||
|             dialog.show(); | ||||
|  | ||||
|             return doSave() | ||||
|                 .then(hideBlockingDialog) | ||||
|                 .then(returnToBrowse) | ||||
|                 .catch(hideBlockingDialog); | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,73 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["./SaveAction"], | ||||
|     function (SaveAction) { | ||||
|  | ||||
|         /** | ||||
|          * The "Save and Stop Editing" action performs a [Save action]{@link SaveAction} | ||||
|          * on the object under edit followed by exiting the edit user interface. | ||||
|          * @constructor | ||||
|          * @implements {Action} | ||||
|          * @memberof platform/commonUI/edit | ||||
|          */ | ||||
|         function SaveAndStopEditingAction( | ||||
|             dialogService, | ||||
|             context | ||||
|         ) { | ||||
|             this.context = context; | ||||
|             this.domainObject = (context || {}).domainObject; | ||||
|             this.dialogService = dialogService; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Trigger a save operation and exit edit mode. | ||||
|          * | ||||
|          * @returns {Promise} a promise that will be fulfilled when | ||||
|          *          cancellation has completed | ||||
|          * @memberof platform/commonUI/edit.SaveAndStopEditingAction# | ||||
|          */ | ||||
|         SaveAndStopEditingAction.prototype.perform = function () { | ||||
|             var domainObject = this.domainObject, | ||||
|                 saveAction = new SaveAction(this.dialogService, this.context); | ||||
|  | ||||
|             function closeEditor() { | ||||
|                 return domainObject.getCapability("editor").finish(); | ||||
|             } | ||||
|  | ||||
|             return saveAction.perform() | ||||
|                 .then(closeEditor) | ||||
|                 .catch(closeEditor); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * 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 | ||||
|          */ | ||||
|         SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo; | ||||
|  | ||||
|         return SaveAndStopEditingAction; | ||||
|     } | ||||
| ); | ||||
| @@ -42,7 +42,6 @@ define([ | ||||
|             $injector, | ||||
|             policyService, | ||||
|             dialogService, | ||||
|             creationService, | ||||
|             copyService, | ||||
|             context | ||||
|         ) { | ||||
| @@ -52,7 +51,6 @@ define([ | ||||
|             }; | ||||
|             this.policyService = policyService; | ||||
|             this.dialogService = dialogService; | ||||
|             this.creationService = creationService; | ||||
|             this.copyService = copyService; | ||||
|         } | ||||
|  | ||||
| @@ -166,11 +164,16 @@ define([ | ||||
|                     .then(resolveWith(object)); | ||||
|             } | ||||
|  | ||||
|             function commitEditingAfterClone(clonedObject) { | ||||
|             function saveAfterClone(clonedObject) { | ||||
|                 return domainObject.getCapability("editor").save() | ||||
|                     .then(resolveWith(clonedObject)); | ||||
|             } | ||||
|  | ||||
|             function finishEditing(clonedObject) { | ||||
|                 return domainObject.getCapability("editor").finish() | ||||
|                     .then(resolveWith(clonedObject)); | ||||
|             } | ||||
|  | ||||
|             function onFailure() { | ||||
|                 hideBlockingDialog(); | ||||
|                 return false; | ||||
| @@ -182,7 +185,8 @@ define([ | ||||
|                 .then(getParent) | ||||
|                 .then(cloneIntoParent) | ||||
|                 .then(undirtyOriginals) | ||||
|                 .then(commitEditingAfterClone) | ||||
|                 .then(saveAfterClone) | ||||
|                 .then(finishEditing) | ||||
|                 .then(hideBlockingDialog) | ||||
|                 .catch(onFailure); | ||||
|         }; | ||||
|   | ||||
| @@ -9,7 +9,8 @@ define([], function () { | ||||
|             title: "Saving...", | ||||
|             hint: "Do not navigate away from this page or close this browser tab while this message is displayed.", | ||||
|             unknownProgress: true, | ||||
|             severity: "info" | ||||
|             severity: "info", | ||||
|             delay: true | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -28,8 +28,8 @@ define( | ||||
|          * A capability that implements an editing 'session' for a domain | ||||
|          * object. An editing session is initiated via a call to .edit(). | ||||
|          * Once initiated, any persist operations will be queued pending a | ||||
|          * subsequent call to [.save()](@link #save) or [.cancel()](@link | ||||
|          * #cancel). | ||||
|          * subsequent call to [.save()](@link #save) or [.finish()](@link | ||||
|          * #finish). | ||||
|          * @param transactionService | ||||
|          * @param domainObject | ||||
|          * @constructor | ||||
| @@ -45,7 +45,7 @@ define( | ||||
|         /** | ||||
|          * Initiate an editing session. This will start a transaction during | ||||
|          * which any persist operations will be deferred until either save() | ||||
|          * or cancel() are called. | ||||
|          * or finish() are called. | ||||
|          */ | ||||
|         EditorCapability.prototype.edit = function () { | ||||
|             this.transactionService.startTransaction(); | ||||
| @@ -81,25 +81,25 @@ define( | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Save any changes from this editing session. This will flush all | ||||
|          * pending persists and end the current transaction | ||||
|          * Save any unsaved changes from this editing session. This will | ||||
|          * end the current transaction and continue with a new one. | ||||
|          * @returns {*} | ||||
|          */ | ||||
|         EditorCapability.prototype.save = function () { | ||||
|             var domainObject = this.domainObject; | ||||
|             return this.transactionService.commit().then(function () { | ||||
|                 domainObject.getCapability('status').set('editing', false); | ||||
|             var transactionService = this.transactionService; | ||||
|             return transactionService.commit().then(function () { | ||||
|                 transactionService.startTransaction(); | ||||
|             }); | ||||
|         }; | ||||
|  | ||||
|         EditorCapability.prototype.invoke = EditorCapability.prototype.edit; | ||||
|  | ||||
|         /** | ||||
|          * Cancel the current editing session. This will discard any pending | ||||
|          * Finish the current editing session. This will discard any pending | ||||
|          * persist operations | ||||
|          * @returns {*} | ||||
|          */ | ||||
|         EditorCapability.prototype.cancel = function () { | ||||
|         EditorCapability.prototype.finish = function () { | ||||
|             var domainObject = this.domainObject; | ||||
|             return this.transactionService.cancel().then(function () { | ||||
|                 domainObject.getCapability("status").set("editing", false); | ||||
|   | ||||
| @@ -81,6 +81,10 @@ define( | ||||
|             return this.persistenceCapability.getSpace(); | ||||
|         }; | ||||
|  | ||||
|         TransactionalPersistenceCapability.prototype.persisted = function () { | ||||
|             return this.persistenceCapability.persisted(); | ||||
|         }; | ||||
|  | ||||
|         return TransactionalPersistenceCapability; | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -27,7 +27,8 @@ define( | ||||
|     [], | ||||
|     function () { | ||||
|  | ||||
|         var ACTION_CONTEXT = { category: 'conclude-editing' }; | ||||
|         var SAVE_ACTION_CONTEXT = { category: 'save' }; | ||||
|         var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' }; | ||||
|  | ||||
|         /** | ||||
|          * Controller which supplies action instances for Save/Cancel. | ||||
| @@ -35,12 +36,37 @@ define( | ||||
|          * @constructor | ||||
|          */ | ||||
|         function EditActionController($scope) { | ||||
|             // Maintain all "conclude-editing" actions in the present | ||||
|             // context. | ||||
|  | ||||
|             function actionToMenuOption(action) { | ||||
|                 return { | ||||
|                     key: action, | ||||
|                     name: action.getMetadata().name, | ||||
|                     cssclass: action.getMetadata().cssclass | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             // Maintain all "conclude-editing" and "save" actions in the | ||||
|             // present context. | ||||
|             function updateActions() { | ||||
|                 $scope.editActions = $scope.action ? | ||||
|                         $scope.action.getActions(ACTION_CONTEXT) : | ||||
|                 $scope.saveActions = $scope.action ? | ||||
|                         $scope.action.getActions(SAVE_ACTION_CONTEXT) : | ||||
|                         []; | ||||
|  | ||||
|                 $scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption); | ||||
|  | ||||
|                 $scope.saveActionMenuClickHandler = function (clickedAction) { | ||||
|                     clickedAction.perform(); | ||||
|                 }; | ||||
|  | ||||
|                 $scope.otherEditActions = $scope.action ? | ||||
|                         $scope.action.getActions(OTHERS_ACTION_CONTEXT) : | ||||
|                         []; | ||||
|  | ||||
|                 // Required because Angular does not allow 'bind' | ||||
|                 // in expressions. | ||||
|                 $scope.actionPerformer = function (action) { | ||||
|                     return action.perform.bind(action); | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             // Update set of actions whenever the action capability | ||||
|   | ||||
| @@ -51,7 +51,7 @@ define( | ||||
|         function AddAction(type, parent, context, $q, dialogService, policyService) { | ||||
|             this.metadata = { | ||||
|                 key: 'add', | ||||
|                 glyph: type.getGlyph(), | ||||
|                 cssclass: type.getCssClass(), | ||||
|                 name: type.getName(), | ||||
|                 type: type.getKey(), | ||||
|                 description: type.getDescription(), | ||||
|   | ||||
| @@ -47,7 +47,7 @@ define( | ||||
|         function CreateAction(type, parent, context) { | ||||
|             this.metadata = { | ||||
|                 key: 'create', | ||||
|                 glyph: type.getGlyph(), | ||||
|                 cssclass: type.getCssClass(), | ||||
|                 name: type.getName(), | ||||
|                 type: type.getKey(), | ||||
|                 description: type.getDescription(), | ||||
| @@ -67,12 +67,17 @@ define( | ||||
|                 editAction, | ||||
|                 editorCapability; | ||||
|  | ||||
|             function closeEditor() { | ||||
|                 return editorCapability.finish(); | ||||
|             } | ||||
|  | ||||
|             function onSave() { | ||||
|                 return editorCapability.save(); | ||||
|                 return editorCapability.save() | ||||
|                     .then(closeEditor); | ||||
|             } | ||||
|  | ||||
|             function onCancel() { | ||||
|                 return editorCapability.cancel(); | ||||
|                 return closeEditor(); | ||||
|             } | ||||
|  | ||||
|             newModel.type = this.type.getKey(); | ||||
| @@ -85,9 +90,9 @@ define( | ||||
|             if (editAction) { | ||||
|                 return editAction.perform(); | ||||
|             } else if (editorCapability) { | ||||
|                 //otherwise, use the save action | ||||
|                 //otherwise, use the save as action | ||||
|                 editorCapability.edit(); | ||||
|                 return newObject.getCapability("action").perform("save").then(onSave, onCancel); | ||||
|                 return newObject.getCapability("action").perform("save-as").then(onSave, onCancel); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -90,7 +90,9 @@ define( | ||||
|             // Ensure there is always a "save in" section | ||||
|             if (includeLocation) { | ||||
|                 sections.push({ | ||||
|                     name: 'Location', rows: [{ | ||||
|                     name: 'Location', | ||||
|                     cssclass: "grows", | ||||
|                     rows: [{ | ||||
|                         name: "Save In", | ||||
|                         control: "locator", | ||||
|                         validate: validateLocation, | ||||
|   | ||||
| @@ -50,10 +50,7 @@ define( | ||||
|                         $scope.rootObject = | ||||
|                             (context && context.getRoot()) || $scope.rootObject; | ||||
|                     }, 0); | ||||
|                 } else if (!contextRoot) { | ||||
|                     //If no context root is available, default to the root | ||||
|                     // object | ||||
|                     $scope.rootObject = undefined; | ||||
|                 } else if (!contextRoot && !$scope.rootObject) { | ||||
|                     // Update the displayed tree on a timeout to avoid | ||||
|                     // an infinite digest exception. | ||||
|                     objectService.getObjects(['ROOT']) | ||||
|   | ||||
| @@ -50,17 +50,13 @@ define( | ||||
|             this.listenHandle = undefined; | ||||
|  | ||||
|             // Mutate and persist a new version of a domain object's model. | ||||
|             function doPersist(model) { | ||||
|             function doMutate(model) { | ||||
|                 var domainObject = self.domainObject; | ||||
|  | ||||
|                 // First, mutate; then, persist. | ||||
|                 return $q.when(domainObject.useCapability("mutation", function () { | ||||
|                     return model; | ||||
|                 })).then(function (result) { | ||||
|                     // Only persist when mutation was successful | ||||
|                     return result && | ||||
|                         domainObject.getCapability("persistence").persist(); | ||||
|                 }); | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
|             // Handle changes to model and/or view configuration | ||||
| @@ -80,14 +76,14 @@ define( | ||||
|                 ].join(" ")); | ||||
|  | ||||
|                 // Update the configuration stored in the model, and persist. | ||||
|                 if (domainObject && domainObject.hasCapability("persistence")) { | ||||
|                 if (domainObject) { | ||||
|                     // Configurations for specific views are stored by | ||||
|                     // key in the "configuration" field of the model. | ||||
|                     if (self.key && configuration) { | ||||
|                         model.configuration = model.configuration || {}; | ||||
|                         model.configuration[self.key] = configuration; | ||||
|                     } | ||||
|                     doPersist(model); | ||||
|                     doMutate(model); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   | ||||
							
								
								
									
										48
									
								
								platform/commonUI/edit/src/services/NestedTransaction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								platform/commonUI/edit/src/services/NestedTransaction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| define(['./Transaction'], function (Transaction) { | ||||
|     /** | ||||
|      * A nested transaction is a transaction which takes place in the context | ||||
|      * of a larger parent transaction. It becomes part of the parent | ||||
|      * transaction when (and only when) committed. | ||||
|      * @param parent | ||||
|      * @constructor | ||||
|      * @extends {platform/commonUI/edit/services.Transaction} | ||||
|      * @memberof platform/commonUI/edit/services | ||||
|      */ | ||||
|     function NestedTransaction(parent) { | ||||
|         this.parent = parent; | ||||
|         Transaction.call(this, parent.$log); | ||||
|     } | ||||
|  | ||||
|     NestedTransaction.prototype = Object.create(Transaction.prototype); | ||||
|  | ||||
|     NestedTransaction.prototype.commit = function () { | ||||
|         this.parent.add( | ||||
|             Transaction.prototype.commit.bind(this), | ||||
|             Transaction.prototype.cancel.bind(this) | ||||
|         ); | ||||
|         return Promise.resolve(true); | ||||
|     }; | ||||
|  | ||||
|     return NestedTransaction; | ||||
| }); | ||||
							
								
								
									
										96
									
								
								platform/commonUI/edit/src/services/Transaction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								platform/commonUI/edit/src/services/Transaction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| define([], function () { | ||||
|     /** | ||||
|      * A Transaction represents a set of changes that are intended to | ||||
|      * be kept or discarded as a unit. | ||||
|      * @param $log Angular's `$log` service, for logging messages | ||||
|      * @constructor | ||||
|      * @memberof platform/commonUI/edit/services | ||||
|      */ | ||||
|     function Transaction($log) { | ||||
|         this.$log = $log; | ||||
|         this.callbacks = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add a change to the current transaction, as expressed by functions | ||||
|      * to either keep or discard the change. | ||||
|      * @param {Function} commit called when the transaction is committed | ||||
|      * @param {Function} cancel called when the transaction is cancelled | ||||
|      * @returns {Function) a function which may be called to remove this | ||||
|      *          pair of callbacks from the transaction | ||||
|      */ | ||||
|     Transaction.prototype.add = function (commit, cancel) { | ||||
|         var callback = { commit: commit, cancel: cancel }; | ||||
|         this.callbacks.push(callback); | ||||
|         return function () { | ||||
|             this.callbacks = this.callbacks.filter(function (c) { | ||||
|                 return c !== callback; | ||||
|             }); | ||||
|         }.bind(this); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get the number of changes in the current transaction. | ||||
|      * @returns {number} the size of the current transaction | ||||
|      */ | ||||
|     Transaction.prototype.size = function () { | ||||
|         return this.callbacks.length; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Keep all changes associated with this transaction. | ||||
|      * @method {platform/commonUI/edit/services.Transaction#commit} | ||||
|      * @returns {Promise} a promise which will resolve when all callbacks | ||||
|      *          have been handled. | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Discard all changes associated with this transaction. | ||||
|      * @method {platform/commonUI/edit/services.Transaction#cancel} | ||||
|      * @returns {Promise} a promise which will resolve when all callbacks | ||||
|      *          have been handled. | ||||
|      */ | ||||
|  | ||||
|     ['commit', 'cancel'].forEach(function (method) { | ||||
|         Transaction.prototype[method] = function () { | ||||
|             var promises = []; | ||||
|             var callback; | ||||
|  | ||||
|             while (this.callbacks.length > 0) { | ||||
|                 callback = this.callbacks.shift(); | ||||
|                 try { | ||||
|                     promises.push(callback[method]()); | ||||
|                 } catch (e) { | ||||
|                     this.$log | ||||
|                         .error("Error trying to " + method + " transaction."); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return Promise.all(promises); | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     return Transaction; | ||||
| }); | ||||
| @@ -21,8 +21,8 @@ | ||||
|  *****************************************************************************/ | ||||
| /*global define*/ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|     ['./Transaction', './NestedTransaction'], | ||||
|     function (Transaction, NestedTransaction) { | ||||
|         /** | ||||
|          * Implements an application-wide transaction state. Once a | ||||
|          * transaction is started, calls to | ||||
| @@ -37,10 +37,7 @@ define( | ||||
|         function TransactionService($q, $log) { | ||||
|             this.$q = $q; | ||||
|             this.$log = $log; | ||||
|             this.transaction = false; | ||||
|  | ||||
|             this.onCommits = []; | ||||
|             this.onCancels = []; | ||||
|             this.transactions = []; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
| @@ -50,18 +47,18 @@ define( | ||||
|          * #cancel} are called | ||||
|          */ | ||||
|         TransactionService.prototype.startTransaction = function () { | ||||
|             if (this.transaction) { | ||||
|                 //Log error because this is a programming error if it occurs. | ||||
|                 this.$log.error("Transaction already in progress"); | ||||
|             } | ||||
|             this.transaction = true; | ||||
|             var transaction = this.isActive() ? | ||||
|                 new NestedTransaction(this.transactions[0]) : | ||||
|                 new Transaction(this.$log); | ||||
|  | ||||
|             this.transactions.push(transaction); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * @returns {boolean} If true, indicates that a transaction is in progress | ||||
|          */ | ||||
|         TransactionService.prototype.isActive = function () { | ||||
|             return this.transaction; | ||||
|             return this.transactions.length > 0; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -72,24 +69,20 @@ define( | ||||
|          * @param onCancel A function to call on cancel | ||||
|          */ | ||||
|         TransactionService.prototype.addToTransaction = function (onCommit, onCancel) { | ||||
|             if (this.transaction) { | ||||
|                 this.onCommits.push(onCommit); | ||||
|                 if (onCancel) { | ||||
|                     this.onCancels.push(onCancel); | ||||
|                 } | ||||
|             if (this.isActive()) { | ||||
|                 return this.activeTransaction().add(onCommit, onCancel); | ||||
|             } else { | ||||
|                 //Log error because this is a programming error if it occurs. | ||||
|                 this.$log.error("No transaction in progress"); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|             return function () { | ||||
|                 this.onCommits = this.onCommits.filter(function (callback) { | ||||
|                     return callback !== onCommit; | ||||
|                 }); | ||||
|                 this.onCancels = this.onCancels.filter(function (callback) { | ||||
|                     return callback !== onCancel; | ||||
|                 }); | ||||
|             }.bind(this); | ||||
|         /** | ||||
|          * Get the transaction at the top of the stack. | ||||
|          * @private | ||||
|          */ | ||||
|         TransactionService.prototype.activeTransaction = function () { | ||||
|             return this.transactions[this.transactions.length - 1]; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -100,24 +93,8 @@ define( | ||||
|          * completed. Will reject if any commit operations fail | ||||
|          */ | ||||
|         TransactionService.prototype.commit = function () { | ||||
|             var self = this, | ||||
|                 promises = [], | ||||
|                 onCommit; | ||||
|  | ||||
|             while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction | ||||
|                 onCommit = this.onCommits.pop(); | ||||
|                 try { // ...also don't want to fail mid-loop... | ||||
|                     promises.push(onCommit()); | ||||
|                 } catch (e) { | ||||
|                     this.$log.error("Error committing transaction."); | ||||
|                 } | ||||
|             } | ||||
|             return this.$q.all(promises).then(function () { | ||||
|                 self.transaction = false; | ||||
|  | ||||
|                 self.onCommits = []; | ||||
|                 self.onCancels = []; | ||||
|             }); | ||||
|             var transaction = this.transactions.pop(); | ||||
|             return transaction ? transaction.commit() : Promise.reject(); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -129,28 +106,17 @@ define( | ||||
|          * @returns {*} | ||||
|          */ | ||||
|         TransactionService.prototype.cancel = function () { | ||||
|             var self = this, | ||||
|                 results = [], | ||||
|                 onCancel; | ||||
|  | ||||
|             while (this.onCancels.length > 0) { | ||||
|                 onCancel = this.onCancels.pop(); | ||||
|                 try { | ||||
|                     results.push(onCancel()); | ||||
|                 } catch (error) { | ||||
|                     this.$log.error("Error committing transaction."); | ||||
|                 } | ||||
|             } | ||||
|             return this.$q.all(results).then(function () { | ||||
|                 self.transaction = false; | ||||
|  | ||||
|                 self.onCommits = []; | ||||
|                 self.onCancels = []; | ||||
|             }); | ||||
|             var transaction = this.transactions.pop(); | ||||
|             return transaction ? transaction.cancel() : Promise.reject(); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Get the size (the number of commit/cancel callbacks) of | ||||
|          * the active transaction. | ||||
|          * @returns {number} size of the active transaction | ||||
|          */ | ||||
|         TransactionService.prototype.size = function () { | ||||
|             return this.onCommits.length; | ||||
|             return this.isActive() ? this.activeTransaction().size() : 0; | ||||
|         }; | ||||
|  | ||||
|         return TransactionService; | ||||
|   | ||||
| @@ -63,7 +63,7 @@ define( | ||||
|  | ||||
|                 capabilities.editor = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     ["save", "cancel", "isEditContextRoot"] | ||||
|                     ["save", "finish", "isEditContextRoot"] | ||||
|                 ); | ||||
|                 capabilities.action = jasmine.createSpyObj( | ||||
|                     "actionCapability", | ||||
| @@ -105,7 +105,7 @@ define( | ||||
|                     return !!capabilities[name]; | ||||
|                 }); | ||||
|  | ||||
|                 capabilities.editor.cancel.andReturn(mockPromise(true)); | ||||
|                 capabilities.editor.finish.andReturn(mockPromise(true)); | ||||
|  | ||||
|                 action = new CancelAction(actionContext); | ||||
|  | ||||
| @@ -130,8 +130,8 @@ define( | ||||
|                 capabilities.action.perform.andReturn(mockPromise(true)); | ||||
|                 action.perform(); | ||||
|  | ||||
|                 // Should have called cancel | ||||
|                 expect(capabilities.editor.cancel).toHaveBeenCalled(); | ||||
|                 // Should have called finish | ||||
|                 expect(capabilities.editor.finish).toHaveBeenCalled(); | ||||
|  | ||||
|                 // Definitely shouldn't call save! | ||||
|                 expect(capabilities.editor.save).not.toHaveBeenCalled(); | ||||
|   | ||||
| @@ -58,7 +58,7 @@ define( | ||||
|                 ); | ||||
|                 mockEditor = jasmine.createSpyObj( | ||||
|                     "editorCapability", | ||||
|                     ["edit", "isEditContextRoot", "cancel"] | ||||
|                     ["edit", "isEditContextRoot"] | ||||
|                 ); | ||||
|  | ||||
|                 capabilities = { | ||||
| @@ -98,13 +98,6 @@ define( | ||||
|                 expect(EditAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it ("cancels editing when user navigates away", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockNavigationService.addListener).toHaveBeenCalled(); | ||||
|                 mockNavigationService.addListener.mostRecentCall.args[0](); | ||||
|                 expect(mockEditor.cancel).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it ("invokes the Edit capability on the object", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor"); | ||||
|   | ||||
| @@ -30,7 +30,6 @@ define( | ||||
|                 mockParent, | ||||
|                 mockContext, | ||||
|                 mockComposition, | ||||
|                 mockPersistence, | ||||
|                 mockActionCapability, | ||||
|                 mockEditAction, | ||||
|                 mockType, | ||||
| @@ -68,7 +67,6 @@ define( | ||||
|                 }; | ||||
|                 mockContext = jasmine.createSpyObj("context", ["getParent"]); | ||||
|                 mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]); | ||||
|                 mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); | ||||
|                 mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]); | ||||
|                 mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]); | ||||
|                 mockEditAction = jasmine.createSpyObj("editAction", ["perform"]); | ||||
| @@ -84,7 +82,6 @@ define( | ||||
|  | ||||
|                 capabilities = { | ||||
|                     composition: mockComposition, | ||||
|                     persistence: mockPersistence, | ||||
|                     action: mockActionCapability, | ||||
|                     type: mockType | ||||
|                 }; | ||||
| @@ -107,11 +104,6 @@ define( | ||||
|                     .toHaveBeenCalledWith(mockDomainObject); | ||||
|             }); | ||||
|  | ||||
|             it("persists changes afterward", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockPersistence.persist).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("enables edit mode for objects that have an edit action", function () { | ||||
|                 mockActionCapability.getActions.andReturn([mockEditAction]); | ||||
|                 action.perform(); | ||||
|   | ||||
| @@ -43,7 +43,6 @@ define( | ||||
|                         }, | ||||
|                         hasFeature: jasmine.createSpy('hasFeature') | ||||
|                     }, | ||||
|                     persistence: jasmine.createSpyObj("persistence", ["persist"]), | ||||
|                     mutation: jasmine.createSpy("mutation") | ||||
|                 }; | ||||
|                 model = {}; | ||||
| @@ -78,25 +77,18 @@ define( | ||||
|                 action = new PropertiesAction(dialogService, context); | ||||
|             }); | ||||
|  | ||||
|             it("persists when an action is performed", function () { | ||||
|                 action.perform(); | ||||
|                 expect(capabilities.persistence.persist) | ||||
|                     .toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("does not persist any changes upon cancel", function () { | ||||
|                 input = undefined; | ||||
|                 action.perform(); | ||||
|                 expect(capabilities.persistence.persist) | ||||
|                     .not.toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("mutates an object when performed", function () { | ||||
|                 action.perform(); | ||||
|                 expect(capabilities.mutation).toHaveBeenCalled(); | ||||
|                 capabilities.mutation.mostRecentCall.args[0]({}); | ||||
|             }); | ||||
|  | ||||
|             it("does not muate object upon cancel", function () { | ||||
|                 input = undefined; | ||||
|                 action.perform(); | ||||
|                 expect(capabilities.mutation).not.toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("is only applicable when a domain object is in context", function () { | ||||
|                 expect(PropertiesAction.appliesTo(context)).toBeTruthy(); | ||||
|                 expect(PropertiesAction.appliesTo({})).toBeFalsy(); | ||||
|   | ||||
| @@ -37,7 +37,6 @@ define( | ||||
|                 mockGrandchildContext, | ||||
|                 mockRootContext, | ||||
|                 mockMutation, | ||||
|                 mockPersistence, | ||||
|                 mockType, | ||||
|                 actionContext, | ||||
|                 model, | ||||
| @@ -53,8 +52,6 @@ define( | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|  | ||||
|  | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     ["getId", "getCapability"] | ||||
| @@ -88,7 +85,6 @@ define( | ||||
|                 mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]); | ||||
|                 mockRootContext = jasmine.createSpyObj("context", ["getParent"]); | ||||
|                 mockMutation = jasmine.createSpyObj("mutation", ["invoke"]); | ||||
|                 mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); | ||||
|                 mockType = jasmine.createSpyObj("type", ["hasFeature"]); | ||||
|                 mockNavigationService = jasmine.createSpyObj( | ||||
|                     "navigationService", | ||||
| @@ -109,7 +105,6 @@ define( | ||||
|  | ||||
|                 capabilities = { | ||||
|                     mutation: mockMutation, | ||||
|                     persistence: mockPersistence, | ||||
|                     type: mockType | ||||
|                 }; | ||||
|                 model = { | ||||
| @@ -118,7 +113,7 @@ define( | ||||
|  | ||||
|                 actionContext = { domainObject: mockDomainObject }; | ||||
|  | ||||
|                 action = new RemoveAction(mockQ, mockNavigationService, actionContext); | ||||
|                 action = new RemoveAction(mockNavigationService, actionContext); | ||||
|             }); | ||||
|  | ||||
|             it("only applies to objects with parents", function () { | ||||
| @@ -154,9 +149,6 @@ define( | ||||
|                 // Should have removed "test" - that was our | ||||
|                 // mock domain object's id. | ||||
|                 expect(result.composition).toEqual(["a", "b"]); | ||||
|  | ||||
|                 // Finally, should have persisted | ||||
|                 expect(mockPersistence.persist).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("removes parent of object currently navigated to", function () { | ||||
|   | ||||
| @@ -56,7 +56,7 @@ define( | ||||
|                 ); | ||||
|                 mockEditorCapability = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     ["save", "cancel", "isEditContextRoot"] | ||||
|                     ["save", "isEditContextRoot"] | ||||
|                 ); | ||||
|                 mockActionCapability = jasmine.createSpyObj( | ||||
|                     "actionCapability", | ||||
| @@ -105,12 +105,6 @@ define( | ||||
|                     expect(mockEditorCapability.save).toHaveBeenCalled(); | ||||
|                 }); | ||||
|  | ||||
|             it("navigates to the object after saving", | ||||
|                 function () { | ||||
|                     action.perform(); | ||||
|                     expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate"); | ||||
|                 }); | ||||
|  | ||||
|             describe("a blocking dialog", function () { | ||||
|                 var mockDialogHandle; | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,123 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/actions/SaveAndStopEditingAction"], | ||||
|     function (SaveAndStopEditingAction) { | ||||
|  | ||||
|         describe("The Save and Stop Editing action", function () { | ||||
|  | ||||
|             // Some mocks appear unused because the | ||||
|             // underlying SaveAction that this action | ||||
|             // depends on is not mocked, so we mock some | ||||
|             // of SaveAction's own dependencies to make | ||||
|             // it run. | ||||
|             var mockDomainObject, | ||||
|                 mockEditorCapability, | ||||
|                 actionContext, | ||||
|                 dialogService, | ||||
|                 mockActionCapability, | ||||
|                 capabilities = {}, | ||||
|                 action; | ||||
|  | ||||
|             function mockPromise(value) { | ||||
|                 return { | ||||
|                     then: function (callback) { | ||||
|                         return mockPromise(callback(value)); | ||||
|                     }, | ||||
|                     catch: function (callback) { | ||||
|                         return mockPromise(callback(value)); | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ | ||||
|                         "getCapability", | ||||
|                         "hasCapability", | ||||
|                         "getModel", | ||||
|                         "getOriginalObject" | ||||
|                     ] | ||||
|                 ); | ||||
|                 mockEditorCapability = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     ["save", "finish", "isEditContextRoot"] | ||||
|                 ); | ||||
|                 mockActionCapability = jasmine.createSpyObj( | ||||
|                     "actionCapability", | ||||
|                     ["perform"] | ||||
|                 ); | ||||
|                 capabilities.editor = mockEditorCapability; | ||||
|                 capabilities.action = mockActionCapability; | ||||
|  | ||||
|                 actionContext = { | ||||
|                     domainObject: mockDomainObject | ||||
|                 }; | ||||
|                 dialogService = jasmine.createSpyObj( | ||||
|                     "dialogService", | ||||
|                     ["showBlockingMessage"] | ||||
|                 ); | ||||
|  | ||||
|                 mockDomainObject.hasCapability.andReturn(true); | ||||
|                 mockDomainObject.getCapability.andCallFake(function (capability) { | ||||
|                     return capabilities[capability]; | ||||
|                 }); | ||||
|                 mockDomainObject.getModel.andReturn({ persisted: 0 }); | ||||
|                 mockEditorCapability.save.andReturn(mockPromise(true)); | ||||
|                 mockEditorCapability.isEditContextRoot.andReturn(true); | ||||
|  | ||||
|                 action = new SaveAndStopEditingAction(dialogService, actionContext); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|             it("only applies to domain object with an editor capability", function () { | ||||
|                 expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true); | ||||
|                 expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); | ||||
|  | ||||
|                 mockDomainObject.hasCapability.andReturn(false); | ||||
|                 mockDomainObject.getCapability.andReturn(undefined); | ||||
|                 expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("only applies to domain object that has already been persisted", function () { | ||||
|                 mockDomainObject.getModel.andReturn({ persisted: undefined }); | ||||
|                 expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("does not close the editor before completing the save", function () { | ||||
|                 mockEditorCapability.save.andReturn(new Promise(function () { | ||||
|                 })); | ||||
|                 action.perform(); | ||||
|                 expect(mockEditorCapability.save).toHaveBeenCalled(); | ||||
|                 expect(mockEditorCapability.finish).not.toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("closes the editor after saving", function () { | ||||
|                 action.perform(); | ||||
|                 expect(mockEditorCapability.save).toHaveBeenCalled(); | ||||
|                 expect(mockEditorCapability.finish).toHaveBeenCalled(); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -19,7 +19,7 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global describe,it,expect,beforeEach,jasmine*/ | ||||
| /*global describe,it,expect,beforeEach,jasmine,runs,waitsFor,spyOn*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/actions/SaveAsAction"], | ||||
| @@ -33,7 +33,6 @@ define( | ||||
|                 mockDialogService, | ||||
|                 mockCopyService, | ||||
|                 mockParent, | ||||
|                 mockUrlService, | ||||
|                 actionContext, | ||||
|                 capabilities = {}, | ||||
|                 action; | ||||
| @@ -78,10 +77,10 @@ define( | ||||
|  | ||||
|                 mockEditorCapability = jasmine.createSpyObj( | ||||
|                     "editor", | ||||
|                     ["save", "cancel", "isEditContextRoot"] | ||||
|                     ["save", "finish", "isEditContextRoot"] | ||||
|                 ); | ||||
|                 mockEditorCapability.cancel.andReturn(mockPromise(undefined)); | ||||
|                 mockEditorCapability.save.andReturn(mockPromise(true)); | ||||
|                 mockEditorCapability.finish.andReturn(mockPromise(true)); | ||||
|                 mockEditorCapability.isEditContextRoot.andReturn(true); | ||||
|                 capabilities.editor = mockEditorCapability; | ||||
|  | ||||
| @@ -113,16 +112,11 @@ define( | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 mockUrlService = jasmine.createSpyObj( | ||||
|                     "urlService", | ||||
|                     ["urlForLocation"] | ||||
|                 ); | ||||
|  | ||||
|                 actionContext = { | ||||
|                     domainObject: mockDomainObject | ||||
|                 }; | ||||
|  | ||||
|                 action = new SaveAsAction(undefined, undefined, mockDialogService, undefined, mockCopyService, actionContext); | ||||
|                 action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext); | ||||
|  | ||||
|                 spyOn(action, "getObjectService"); | ||||
|                 action.getObjectService.andReturn(mockObjectService); | ||||
| @@ -156,6 +150,28 @@ define( | ||||
|                 expect(SaveAsAction.appliesTo(actionContext)).toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("uses the editor capability to save the object", function () { | ||||
|                 mockEditorCapability.save.andReturn(new Promise(function () {})); | ||||
|                 runs(function () { | ||||
|                     action.perform(); | ||||
|                 }); | ||||
|                 waitsFor(function () { | ||||
|                     return mockEditorCapability.save.calls.length > 0; | ||||
|                 }, "perform() should call EditorCapability.save"); | ||||
|                 runs(function () { | ||||
|                     expect(mockEditorCapability.finish).not.toHaveBeenCalled(); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("uses the editor capability to finish editing the object", function () { | ||||
|                 runs(function () { | ||||
|                     action.perform(); | ||||
|                 }); | ||||
|                 waitsFor(function () { | ||||
|                     return mockEditorCapability.finish.calls.length > 0; | ||||
|                 }, "perform() should call EditorCapability.finish"); | ||||
|             }); | ||||
|  | ||||
|             it("returns to browse after save", function () { | ||||
|                 spyOn(action, "save"); | ||||
|                 action.save.andReturn(mockPromise(mockDomainObject)); | ||||
|   | ||||
| @@ -134,15 +134,15 @@ define( | ||||
|                 it("commits the transaction", function () { | ||||
|                     expect(mockTransactionService.commit).toHaveBeenCalled(); | ||||
|                 }); | ||||
|                 it("resets the edit state", function () { | ||||
|                     expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false); | ||||
|                 it("begins a new transaction", function () { | ||||
|                     expect(mockTransactionService.startTransaction).toHaveBeenCalled(); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             describe("cancel", function () { | ||||
|             describe("finish", function () { | ||||
|                 beforeEach(function () { | ||||
|                     capability.edit(); | ||||
|                     capability.cancel(); | ||||
|                     capability.finish(); | ||||
|                 }); | ||||
|                 it("cancels the transaction", function () { | ||||
|                     expect(mockTransactionService.cancel).toHaveBeenCalled(); | ||||
| @@ -158,7 +158,7 @@ define( | ||||
|                 beforeEach(function () { | ||||
|                     mockDomainObject.getModel.andReturn(model); | ||||
|                     capability.edit(); | ||||
|                     capability.cancel(); | ||||
|                     capability.finish(); | ||||
|                 }); | ||||
|                 it("returns true if the object has been modified since it" + | ||||
|                     " was last persisted", function () { | ||||
|   | ||||
| @@ -19,22 +19,51 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| /*global describe,it,expect,beforeEach,jasmine*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/controllers/EditActionController"], | ||||
|     function (EditActionController) { | ||||
|  | ||||
|         describe("The Edit Action controller", function () { | ||||
|             var mockSaveActionMetadata = { | ||||
|                 name: "mocked-save-action", | ||||
|                 cssclass: "mocked-save-action-css" | ||||
|             }; | ||||
|  | ||||
|             function fakeGetActions(actionContext) { | ||||
|                 if (actionContext.category === "save") { | ||||
|                     var mockedSaveActions = [ | ||||
|                         jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]), | ||||
|                         jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]) | ||||
|                     ]; | ||||
|                     mockedSaveActions.forEach(function (action) { | ||||
|                         action.getMetadata.andReturn(mockSaveActionMetadata); | ||||
|                     }); | ||||
|                     return mockedSaveActions; | ||||
|                 } else if (actionContext.category === "conclude-editing") { | ||||
|                     return ["a", "b", "c"]; | ||||
|                 } else { | ||||
|                     throw "EditActionController uses a context that's not covered by tests."; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var mockScope, | ||||
|                 mockActions, | ||||
|                 controller; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockActions = jasmine.createSpyObj("action", ["getActions"]); | ||||
|                 mockActions.getActions.andCallFake(fakeGetActions); | ||||
|                 mockScope = jasmine.createSpyObj("$scope", ["$watch"]); | ||||
|                 mockScope.action = mockActions; | ||||
|                 controller = new EditActionController(mockScope); | ||||
|             }); | ||||
|  | ||||
|             function makeControllerUpdateActions() { | ||||
|                 mockScope.$watch.mostRecentCall.args[1](); | ||||
|             } | ||||
|  | ||||
|             it("watches scope that may change applicable actions", function () { | ||||
|                 // The action capability | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
| @@ -43,16 +72,34 @@ define( | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("populates the scope with grouped and ungrouped actions", function () { | ||||
|                 mockScope.action = mockActions; | ||||
|             it("populates the scope with 'save' actions", function () { | ||||
|                 makeControllerUpdateActions(); | ||||
|                 expect(mockScope.saveActions.length).toEqual(2); | ||||
|             }); | ||||
|  | ||||
|                 mockActions.getActions.andReturn(["a", "b", "c"]); | ||||
|             it("converts 'save' actions to their menu counterparts", function () { | ||||
|                 makeControllerUpdateActions(); | ||||
|                 var menuOptions = mockScope.saveActionsAsMenuOptions; | ||||
|  | ||||
|                 // Call the watch | ||||
|                 mockScope.$watch.mostRecentCall.args[1](); | ||||
|                 expect(menuOptions.length).toEqual(2); | ||||
|                 expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]); | ||||
|                 expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]); | ||||
|                 menuOptions.forEach(function (option) { | ||||
|                     expect(option.name).toEqual(mockSaveActionMetadata.name); | ||||
|                     expect(option.cssclass).toEqual(mockSaveActionMetadata.cssclass); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|                 // Should have grouped and ungrouped actions in scope now | ||||
|                 expect(mockScope.editActions.length).toEqual(3); | ||||
|             it("uses a click handler to perform the clicked action", function () { | ||||
|                 makeControllerUpdateActions(); | ||||
|                 var sampleSaveAction = mockScope.saveActions[0]; | ||||
|                 mockScope.saveActionMenuClickHandler(sampleSaveAction); | ||||
|                 expect(sampleSaveAction.perform).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("populates the scope with other editing actions", function () { | ||||
|                 makeControllerUpdateActions(); | ||||
|                 expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -45,6 +45,7 @@ define( | ||||
|                     [ | ||||
|                         "getKey", | ||||
|                         "getGlyph", | ||||
|                         "getCssClass", | ||||
|                         "getName", | ||||
|                         "getDescription", | ||||
|                         "getProperties", | ||||
|   | ||||
| @@ -41,6 +41,7 @@ define( | ||||
|                     [ | ||||
|                         "getKey", | ||||
|                         "getGlyph", | ||||
|                         "getCssClass", | ||||
|                         "getName", | ||||
|                         "getDescription", | ||||
|                         "getProperties", | ||||
|   | ||||
| @@ -51,6 +51,7 @@ define( | ||||
|                     [ | ||||
|                         "getKey", | ||||
|                         "getGlyph", | ||||
|                         "getCssClass", | ||||
|                         "getName", | ||||
|                         "getDescription", | ||||
|                         "getProperties", | ||||
| @@ -102,7 +103,7 @@ define( | ||||
|                     [ | ||||
|                         "edit", | ||||
|                         "save", | ||||
|                         "cancel" | ||||
|                         "finish" | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
| @@ -119,7 +120,7 @@ define( | ||||
|                 mockParent.useCapability.andReturn(mockDomainObject); | ||||
|  | ||||
|                 mockType.getKey.andReturn("test"); | ||||
|                 mockType.getGlyph.andReturn("T"); | ||||
|                 mockType.getCssClass.andReturn("icon-telemetry"); | ||||
|                 mockType.getDescription.andReturn("a test type"); | ||||
|                 mockType.getName.andReturn("Test"); | ||||
|                 mockType.getProperties.andReturn([]); | ||||
| @@ -137,10 +138,11 @@ define( | ||||
|  | ||||
|                 expect(metadata.name).toEqual("Test"); | ||||
|                 expect(metadata.description).toEqual("a test type"); | ||||
|                 expect(metadata.glyph).toEqual("T"); | ||||
|                 expect(metadata.cssclass).toEqual("icon-telemetry"); | ||||
|             }); | ||||
|  | ||||
|             describe("the perform function", function () { | ||||
|                 var promise = jasmine.createSpyObj("promise", ["then"]); | ||||
|                 beforeEach(function () { | ||||
|                     capabilities.action.getActions.andReturn([mockEditAction]); | ||||
|                 }); | ||||
| @@ -155,19 +157,20 @@ define( | ||||
|                     expect(mockEditAction.perform).toHaveBeenCalled(); | ||||
|                 }); | ||||
|  | ||||
|                 it("uses the save action if object does not have an edit action" + | ||||
|                 it("uses the save-as action if object does not have an edit action" + | ||||
|                     " available", function () { | ||||
|                     capabilities.action.getActions.andReturn([]); | ||||
|                     capabilities.action.perform.andReturn(mockPromise(undefined)); | ||||
|                     capabilities.editor.save.andReturn(promise); | ||||
|                     action.perform(); | ||||
|                     expect(capabilities.action.perform).toHaveBeenCalledWith("save"); | ||||
|                     expect(capabilities.action.perform).toHaveBeenCalledWith("save-as"); | ||||
|                 }); | ||||
|  | ||||
|                 describe("uses to editor capability", function () { | ||||
|                     var promise = jasmine.createSpyObj("promise", ["then"]); | ||||
|                     beforeEach(function () { | ||||
|                         capabilities.action.getActions.andReturn([]); | ||||
|                         capabilities.action.perform.andReturn(promise); | ||||
|                         capabilities.editor.save.andReturn(promise); | ||||
|                     }); | ||||
|  | ||||
|                     it("to save the edit if user saves dialog", function () { | ||||
| @@ -177,10 +180,10 @@ define( | ||||
|                         expect(capabilities.editor.save).toHaveBeenCalled(); | ||||
|                     }); | ||||
|  | ||||
|                     it("to cancel the edit if user cancels dialog", function () { | ||||
|                     it("to finish the edit if user cancels dialog", function () { | ||||
|                         action.perform(); | ||||
|                         promise.then.mostRecentCall.args[1](); | ||||
|                         expect(capabilities.editor.cancel).toHaveBeenCalled(); | ||||
|                         expect(capabilities.editor.finish).toHaveBeenCalled(); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|   | ||||
| @@ -54,6 +54,7 @@ define( | ||||
|                     [ | ||||
|                         "getKey", | ||||
|                         "getGlyph", | ||||
|                         "getCssClass", | ||||
|                         "getName", | ||||
|                         "getDescription", | ||||
|                         "getProperties", | ||||
| @@ -74,7 +75,7 @@ define( | ||||
|                 testModel = { someKey: "some value" }; | ||||
|  | ||||
|                 mockType.getKey.andReturn("test"); | ||||
|                 mockType.getGlyph.andReturn("T"); | ||||
|                 mockType.getCssClass.andReturn("icon-telemetry"); | ||||
|                 mockType.getDescription.andReturn("a test type"); | ||||
|                 mockType.getName.andReturn("Test"); | ||||
|                 mockType.getInitialModel.andReturn(testModel); | ||||
|   | ||||
| @@ -195,7 +195,7 @@ define( | ||||
|                 expect(failure).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("logs an error when mutaton fails", function () { | ||||
|             it("logs an error when mutation fails", function () { | ||||
|                 // If mutation of the parent fails, we've lost the | ||||
|                 // created object - this is an error. | ||||
|                 var model = { someKey: "some value" }; | ||||
|   | ||||
| @@ -138,23 +138,34 @@ define( | ||||
|                     }); | ||||
|             }); | ||||
|             describe("when no context is available", function () { | ||||
|                     var defaultRoot = "DEFAULT_ROOT"; | ||||
|  | ||||
|                     beforeEach(function () { | ||||
|                         mockContext.getRoot.andReturn(undefined); | ||||
|                         getObjectsPromise.then.andCallFake(function (callback) { | ||||
|                             callback({'ROOT': defaultRoot}); | ||||
|                         }); | ||||
|                         controller = new LocatorController(mockScope, mockTimeout, mockObjectService); | ||||
|                     }); | ||||
|  | ||||
|                     it("provides a default context where none is available", function () { | ||||
|                         mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                         mockTimeout.mostRecentCall.args[0](); | ||||
|                         expect(mockScope.rootObject).toBe(defaultRoot); | ||||
|                 var defaultRoot = "DEFAULT_ROOT"; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     mockContext.getRoot.andReturn(undefined); | ||||
|                     getObjectsPromise.then.andCallFake(function (callback) { | ||||
|                         callback({'ROOT': defaultRoot}); | ||||
|                     }); | ||||
|                     controller = new LocatorController(mockScope, mockTimeout, mockObjectService); | ||||
|                 }); | ||||
|  | ||||
|                 it("provides a default context where none is available", function () { | ||||
|                     mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                     mockTimeout.mostRecentCall.args[0](); | ||||
|                     expect(mockScope.rootObject).toBe(defaultRoot); | ||||
|                 }); | ||||
|  | ||||
|                 it("does not issue redundant requests for the root object", function () { | ||||
|                     mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                     mockTimeout.mostRecentCall.args[0](); | ||||
|                     mockScope.$watch.mostRecentCall.args[1](undefined); | ||||
|                     mockTimeout.mostRecentCall.args[0](); | ||||
|                     mockScope.$watch.mostRecentCall.args[1](mockDomainObject); | ||||
|                     mockTimeout.mostRecentCall.args[0](); | ||||
|                     expect(mockObjectService.getObjects.calls.length) | ||||
|                         .toEqual(1); | ||||
|                 }); | ||||
|  | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -30,7 +30,6 @@ define( | ||||
|                 mockScope, | ||||
|                 testRepresentation, | ||||
|                 mockDomainObject, | ||||
|                 mockPersistence, | ||||
|                 mockStatusCapability, | ||||
|                 mockEditorCapability, | ||||
|                 mockCapabilities, | ||||
| @@ -56,15 +55,12 @@ define( | ||||
|                     "useCapability", | ||||
|                     "hasCapability" | ||||
|                 ]); | ||||
|                 mockPersistence = | ||||
|                     jasmine.createSpyObj("persistence", ["persist"]); | ||||
|                 mockStatusCapability = | ||||
|                     jasmine.createSpyObj("statusCapability", ["listen"]); | ||||
|                 mockEditorCapability = | ||||
|                     jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]); | ||||
|  | ||||
|                 mockCapabilities = { | ||||
|                     'persistence': mockPersistence, | ||||
|                     'status': mockStatusCapability, | ||||
|                     'editor': mockEditorCapability | ||||
|                 }; | ||||
| @@ -96,7 +92,7 @@ define( | ||||
|                 expect(representer.listenHandle).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("mutates and persists upon observed changes", function () { | ||||
|             it("mutates upon observed changes", function () { | ||||
|                 mockScope.model = { someKey: "some value" }; | ||||
|                 mockScope.configuration = { someConfiguration: "something" }; | ||||
|  | ||||
| @@ -108,11 +104,8 @@ define( | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 // ... and should have persisted the mutation | ||||
|                 expect(mockPersistence.persist).toHaveBeenCalled(); | ||||
|  | ||||
|                 // Finally, check that the provided mutation function | ||||
|                 // includes both model and configuratioon | ||||
|                 // includes both model and configuration | ||||
|                 expect( | ||||
|                     mockDomainObject.useCapability.mostRecentCall.args[1]() | ||||
|                 ).toEqual({ | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user