Compare commits
	
		
			1010 Commits
		
	
	
		
			open-v0.7.
			...
			open187
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d7ccf5105b | ||
| 
						 | 
					73f8a06695 | ||
| 
						 | 
					9cc0c0b06f | ||
| 
						 | 
					c703714cb3 | ||
| 
						 | 
					b48a07cd3e | ||
| 
						 | 
					8c29c8ed0e | ||
| 
						 | 
					5763511ec8 | ||
| 
						 | 
					2accf21518 | ||
| 
						 | 
					28c42fcd4c | ||
| 
						 | 
					1efa97e6f5 | ||
| 
						 | 
					756f728865 | ||
| 
						 | 
					d8276c532b | ||
| 
						 | 
					fc0bfa77db | ||
| 
						 | 
					12c6e53939 | ||
| 
						 | 
					4f716ad5c5 | ||
| 
						 | 
					8093fcbda1 | ||
| 
						 | 
					0ea5721f76 | ||
| 
						 | 
					d902943552 | ||
| 
						 | 
					5f7c8ccadb | ||
| 
						 | 
					520d17f9db | ||
| 
						 | 
					7532db5f49 | ||
| 
						 | 
					ebd8fdeee3 | ||
| 
						 | 
					f738f84075 | ||
| 
						 | 
					cc19a0acba | ||
| 
						 | 
					134b749bbf | ||
| 
						 | 
					36d06e8b54 | ||
| 
						 | 
					5520d90984 | ||
| 
						 | 
					77b0086d18 | ||
| 
						 | 
					146e948097 | ||
| 
						 | 
					669b434c36 | ||
| 
						 | 
					431c74ca49 | ||
| 
						 | 
					cd0c0f77cc | ||
| 
						 | 
					8cba321886 | ||
| 
						 | 
					dd83816035 | ||
| 
						 | 
					1ca2b769d9 | ||
| 
						 | 
					a1d1261179 | ||
| 
						 | 
					99048a4ee3 | ||
| 
						 | 
					553b17fafe | ||
| 
						 | 
					c4aff95341 | ||
| 
						 | 
					13095b4135 | ||
| 
						 | 
					fd927d4c03 | ||
| 
						 | 
					445f22ccb0 | ||
| 
						 | 
					1ad0bf337c | ||
| 
						 | 
					73dc16d398 | ||
| 
						 | 
					bebe53820f | ||
| 
						 | 
					dfe909d6b5 | ||
| 
						 | 
					3cf62ded08 | ||
| 
						 | 
					6cbd3e5fae | ||
| 
						 | 
					3050b265fb | ||
| 
						 | 
					5104a7990a | ||
| 
						 | 
					571beb8df2 | ||
| 
						 | 
					bea5002752 | ||
| 
						 | 
					d04c5e6858 | ||
| 
						 | 
					0d1f3bf87a | ||
| 
						 | 
					b632926d8e | ||
| 
						 | 
					78f3f8367e | ||
| 
						 | 
					85ac4a9a32 | ||
| 
						 | 
					ef527df381 | ||
| 
						 | 
					c2868a4573 | ||
| 
						 | 
					77c66053f3 | ||
| 
						 | 
					0891e15936 | ||
| 
						 | 
					2979ee90a3 | ||
| 
						 | 
					77c399f2a2 | ||
| 
						 | 
					fe8543158e | ||
| 
						 | 
					a4f3e0d776 | ||
| 
						 | 
					866c8882ca | ||
| 
						 | 
					ad7d3d642e | ||
| 
						 | 
					333f7cb848 | ||
| 
						 | 
					f198c281bc | ||
| 
						 | 
					d6fe543c16 | ||
| 
						 | 
					581dbacc9f | ||
| 
						 | 
					23de3917bb | ||
| 
						 | 
					badaca53d3 | ||
| 
						 | 
					00ac249ee2 | ||
| 
						 | 
					00aa6821d1 | ||
| 
						 | 
					e33485ec59 | ||
| 
						 | 
					afb1202865 | ||
| 
						 | 
					5b02bc8104 | ||
| 
						 | 
					f5a4a370f9 | ||
| 
						 | 
					2848a8458b | ||
| 
						 | 
					4f0e8ada2f | ||
| 
						 | 
					cbaf45afe9 | ||
| 
						 | 
					7a677062e4 | ||
| 
						 | 
					a7153f320f | ||
| 
						 | 
					b3da6edd0c | ||
| 
						 | 
					ff1fd26efc | ||
| 
						 | 
					4ced6c44a6 | ||
| 
						 | 
					67f627b51f | ||
| 
						 | 
					1d83516982 | ||
| 
						 | 
					9e64dfe3b9 | ||
| 
						 | 
					09f5fa42ab | ||
| 
						 | 
					54a077a4e2 | ||
| 
						 | 
					13525a67c2 | ||
| 
						 | 
					cc6b6538d5 | ||
| 
						 | 
					0c7de98195 | ||
| 
						 | 
					404d1e7801 | ||
| 
						 | 
					1214a32c26 | ||
| 
						 | 
					6bd8e7a47c | ||
| 
						 | 
					825d93cee3 | ||
| 
						 | 
					9f7dc1da9b | ||
| 
						 | 
					3d8aec2d01 | ||
| 
						 | 
					928e31b548 | ||
| 
						 | 
					f182d1f2c4 | ||
| 
						 | 
					d238b669a5 | ||
| 
						 | 
					5d5a7c26c5 | ||
| 
						 | 
					0b0cee3afb | ||
| 
						 | 
					0260e6fff4 | ||
| 
						 | 
					9811443c71 | ||
| 
						 | 
					7dc13dab66 | ||
| 
						 | 
					e67a2e63cf | ||
| 
						 | 
					6ce4ce6ada | ||
| 
						 | 
					70bc17c79f | ||
| 
						 | 
					f3aceb1644 | ||
| 
						 | 
					056b0c8c5b | ||
| 
						 | 
					fa60d62039 | ||
| 
						 | 
					082122ddec | ||
| 
						 | 
					6dbccd5000 | ||
| 
						 | 
					fb0861f256 | ||
| 
						 | 
					dbe1875e70 | ||
| 
						 | 
					97892869ae | ||
| 
						 | 
					8db334e45b | ||
| 
						 | 
					4743833f7c | ||
| 
						 | 
					6996883b85 | ||
| 
						 | 
					119403e71c | ||
| 
						 | 
					ff24f06475 | ||
| 
						 | 
					597b18af1c | ||
| 
						 | 
					aba0969312 | ||
| 
						 | 
					b4a2bfd727 | ||
| 
						 | 
					2040304980 | ||
| 
						 | 
					55362f6b26 | ||
| 
						 | 
					8611926533 | ||
| 
						 | 
					c17269ba8b | ||
| 
						 | 
					56ccf29cfe | ||
| 
						 | 
					8b7a94dcf8 | ||
| 
						 | 
					3e5d294a28 | ||
| 
						 | 
					480da3476e | ||
| 
						 | 
					54334a8906 | ||
| 
						 | 
					c9b2f5804d | ||
| 
						 | 
					0c1f77cfab | ||
| 
						 | 
					ae4313253c | ||
| 
						 | 
					1e6c4732f1 | ||
| 
						 | 
					884d36ad6d | ||
| 
						 | 
					24586ae378 | ||
| 
						 | 
					cf97b30084 | ||
| 
						 | 
					e86e61c2ac | ||
| 
						 | 
					b163b45a5e | ||
| 
						 | 
					d236fd10c3 | ||
| 
						 | 
					befa66a9e2 | ||
| 
						 | 
					5a1c83fca8 | ||
| 
						 | 
					88bb213162 | ||
| 
						 | 
					ad552511c1 | ||
| 
						 | 
					65368ed0c3 | ||
| 
						 | 
					bf05e55d02 | ||
| 
						 | 
					c057ba38a5 | ||
| 
						 | 
					70bcb723ef | ||
| 
						 | 
					4c5808c8c9 | ||
| 
						 | 
					fa186a1556 | ||
| 
						 | 
					0b4f44f00d | ||
| 
						 | 
					dce7253b27 | ||
| 
						 | 
					158fbf5365 | ||
| 
						 | 
					1d1589da68 | ||
| 
						 | 
					6aba34dd5a | ||
| 
						 | 
					733a282f3f | ||
| 
						 | 
					19500c2704 | ||
| 
						 | 
					2090b1a958 | ||
| 
						 | 
					411a4ebfbf | ||
| 
						 | 
					cc3b0ecfca | ||
| 
						 | 
					ac48039b90 | ||
| 
						 | 
					411f0d904d | ||
| 
						 | 
					d92ae4d508 | ||
| 
						 | 
					bc4c7feb6c | ||
| 
						 | 
					c4aed57165 | ||
| 
						 | 
					88b8528aaf | ||
| 
						 | 
					d1c0d81120 | ||
| 
						 | 
					a233856bac | ||
| 
						 | 
					9a7f435b61 | ||
| 
						 | 
					f4e53a946d | ||
| 
						 | 
					9fdb9f0170 | ||
| 
						 | 
					f28d2f19d0 | ||
| 
						 | 
					de71bde62f | ||
| 
						 | 
					8f24e014e0 | ||
| 
						 | 
					190f5fd0ea | ||
| 
						 | 
					ad29fb0f92 | ||
| 
						 | 
					acdd35cf65 | ||
| 
						 | 
					fcd073c010 | ||
| 
						 | 
					071368c3b9 | ||
| 
						 | 
					4f5a1115d2 | ||
| 
						 | 
					7a97588aa5 | ||
| 
						 | 
					f776561303 | ||
| 
						 | 
					e34fe1a289 | ||
| 
						 | 
					70d9587c9b | ||
| 
						 | 
					9a78b63065 | ||
| 
						 | 
					6c497f3c36 | ||
| 
						 | 
					d951b794e3 | ||
| 
						 | 
					797046aca4 | ||
| 
						 | 
					cf76583ed7 | ||
| 
						 | 
					6f28ab0145 | ||
| 
						 | 
					d981e7bb1d | ||
| 
						 | 
					a3a6706869 | ||
| 
						 | 
					72c122e0ee | ||
| 
						 | 
					9ebf157ec0 | ||
| 
						 | 
					493c63be44 | ||
| 
						 | 
					f29951140f | ||
| 
						 | 
					d0b5bb2d21 | ||
| 
						 | 
					cd98886a43 | ||
| 
						 | 
					4549828cae | ||
| 
						 | 
					d0478c3433 | ||
| 
						 | 
					53369ec0dc | ||
| 
						 | 
					de99969f0a | ||
| 
						 | 
					24449d2dcc | ||
| 
						 | 
					f42c5ca1e5 | ||
| 
						 | 
					8c1b70f085 | ||
| 
						 | 
					c010011798 | ||
| 
						 | 
					890aafc203 | ||
| 
						 | 
					ecf7f4b235 | ||
| 
						 | 
					3e5d46a62b | ||
| 
						 | 
					01045e6ee1 | ||
| 
						 | 
					febb0edede | ||
| 
						 | 
					048eb20450 | ||
| 
						 | 
					36e38b9454 | ||
| 
						 | 
					215b4a154b | ||
| 
						 | 
					47bfe40294 | ||
| 
						 | 
					3a3829682c | ||
| 
						 | 
					861ea8bab7 | ||
| 
						 | 
					f17410a85b | ||
| 
						 | 
					2a14cf2dfc | ||
| 
						 | 
					62962e119e | ||
| 
						 | 
					a840b59044 | ||
| 
						 | 
					d14c4e08b8 | ||
| 
						 | 
					de6dfe1fdc | ||
| 
						 | 
					2229e868ce | ||
| 
						 | 
					3b2454c6a9 | ||
| 
						 | 
					8d209f4d19 | ||
| 
						 | 
					86bb89a162 | ||
| 
						 | 
					2758250833 | ||
| 
						 | 
					7d20351a6a | ||
| 
						 | 
					78fae345da | ||
| 
						 | 
					4c79c9a1b1 | ||
| 
						 | 
					2ec9956d44 | ||
| 
						 | 
					426ab44d93 | ||
| 
						 | 
					3792dab0f9 | ||
| 
						 | 
					c6df7cebe5 | ||
| 
						 | 
					52df7fe1e2 | ||
| 
						 | 
					e3b191b5dc | ||
| 
						 | 
					32b895ba62 | ||
| 
						 | 
					a4dda695dd | ||
| 
						 | 
					4db48afab0 | ||
| 
						 | 
					0d710209b1 | ||
| 
						 | 
					52c471bd3a | ||
| 
						 | 
					560c8612bd | ||
| 
						 | 
					6a917f5393 | ||
| 
						 | 
					fdbc91131b | ||
| 
						 | 
					d2dfec3ce7 | ||
| 
						 | 
					351181d38e | ||
| 
						 | 
					760f4b818f | ||
| 
						 | 
					c026bfa17d | ||
| 
						 | 
					47b97a504e | ||
| 
						 | 
					86deec6364 | ||
| 
						 | 
					ac41ed7d64 | ||
| 
						 | 
					dafcda281a | ||
| 
						 | 
					29c460556a | ||
| 
						 | 
					4d276888e1 | ||
| 
						 | 
					142af3db77 | ||
| 
						 | 
					b66759e519 | ||
| 
						 | 
					c58ffb4a52 | ||
| 
						 | 
					600ff1a3ee | ||
| 
						 | 
					77d11e1bcf | ||
| 
						 | 
					d158aa6028 | ||
| 
						 | 
					c2985d61b7 | ||
| 
						 | 
					3ce40ab870 | ||
| 
						 | 
					bfb19dea74 | ||
| 
						 | 
					01a6d2e6a7 | ||
| 
						 | 
					cc5908d26f | ||
| 
						 | 
					af462ff3ee | ||
| 
						 | 
					5c1d209eff | ||
| 
						 | 
					8a76c3a425 | ||
| 
						 | 
					9ccd0b9188 | ||
| 
						 | 
					f83588d980 | ||
| 
						 | 
					a481b377cb | ||
| 
						 | 
					35ff4efbca | ||
| 
						 | 
					436e010738 | ||
| 
						 | 
					37c34c668c | ||
| 
						 | 
					bf4765fcb6 | ||
| 
						 | 
					dbfb8b9861 | ||
| 
						 | 
					681cd0bb9c | ||
| 
						 | 
					b668fb58fb | ||
| 
						 | 
					f74da6b935 | ||
| 
						 | 
					e4dec21ceb | ||
| 
						 | 
					fc2860810b | ||
| 
						 | 
					62898c921f | ||
| 
						 | 
					4997fc1417 | ||
| 
						 | 
					bb1a02c8bd | ||
| 
						 | 
					9d6b70f433 | ||
| 
						 | 
					57a947eaef | ||
| 
						 | 
					a18cc50a43 | ||
| 
						 | 
					91fe3d798f | ||
| 
						 | 
					e873389655 | ||
| 
						 | 
					678d11832c | ||
| 
						 | 
					3310016264 | ||
| 
						 | 
					17e9e87a2b | ||
| 
						 | 
					b2649de649 | ||
| 
						 | 
					b1238b0c96 | ||
| 
						 | 
					ba6e542d08 | ||
| 
						 | 
					8759fdbd95 | ||
| 
						 | 
					d3d94d67ea | ||
| 
						 | 
					b9d8b124ff | ||
| 
						 | 
					8e995eba8f | ||
| 
						 | 
					194bc41322 | ||
| 
						 | 
					2f57f47234 | ||
| 
						 | 
					59e181c0e5 | ||
| 
						 | 
					8a5c61e2e6 | ||
| 
						 | 
					199be71f8f | ||
| 
						 | 
					ac2144d5c5 | ||
| 
						 | 
					01f2fab70f | ||
| 
						 | 
					7562c82369 | ||
| 
						 | 
					c3efb29dfc | ||
| 
						 | 
					bb738f9a93 | ||
| 
						 | 
					97071fc72c | ||
| 
						 | 
					b39753d0a6 | ||
| 
						 | 
					e3eda5112c | ||
| 
						 | 
					2cbe686732 | ||
| 
						 | 
					021d105fd0 | ||
| 
						 | 
					7ffbc9ad2f | ||
| 
						 | 
					90f62b92a5 | ||
| 
						 | 
					5a991076ea | ||
| 
						 | 
					eb4ea8f2d4 | ||
| 
						 | 
					9692596859 | ||
| 
						 | 
					ce152881cc | ||
| 
						 | 
					f40e7d7105 | ||
| 
						 | 
					a8fc5ee8ed | ||
| 
						 | 
					5400f51204 | ||
| 
						 | 
					29c9b2a08f | ||
| 
						 | 
					e4d136d345 | ||
| 
						 | 
					620c0415cf | ||
| 
						 | 
					54bf39344a | ||
| 
						 | 
					683b3f7511 | ||
| 
						 | 
					510e7d1261 | ||
| 
						 | 
					63b41d796a | ||
| 
						 | 
					80a91970f6 | ||
| 
						 | 
					f0c64b6b92 | ||
| 
						 | 
					48136c2265 | ||
| 
						 | 
					1961adf8d6 | ||
| 
						 | 
					5230bdfc6b | ||
| 
						 | 
					97acf2012a | ||
| 
						 | 
					687d86790e | ||
| 
						 | 
					3ec4cc099b | ||
| 
						 | 
					dadbf3f6dc | ||
| 
						 | 
					c4c4c42415 | ||
| 
						 | 
					8c4c65241a | ||
| 
						 | 
					67cb5f8077 | ||
| 
						 | 
					bb2f086623 | ||
| 
						 | 
					a0169ad158 | ||
| 
						 | 
					6e172359b4 | ||
| 
						 | 
					d33678276c | ||
| 
						 | 
					1cbca41c53 | ||
| 
						 | 
					a27083c28c | ||
| 
						 | 
					def5e17bb8 | ||
| 
						 | 
					2cd1907b71 | ||
| 
						 | 
					b8b9721ddc | ||
| 
						 | 
					f80266b23f | ||
| 
						 | 
					9a3009f327 | ||
| 
						 | 
					2542255342 | ||
| 
						 | 
					e7563ff4e9 | ||
| 
						 | 
					89bfa54563 | ||
| 
						 | 
					a507557cec | ||
| 
						 | 
					793c99ac2f | ||
| 
						 | 
					39bd292886 | ||
| 
						 | 
					e8bc9ecc1a | ||
| 
						 | 
					0fa330adff | ||
| 
						 | 
					509badb225 | ||
| 
						 | 
					9cd57614e4 | ||
| 
						 | 
					d80c359a86 | ||
| 
						 | 
					07d0706cd1 | ||
| 
						 | 
					fd5a911d50 | ||
| 
						 | 
					c71f1fe447 | ||
| 
						 | 
					cd46dab5c1 | ||
| 
						 | 
					e1858bf0ae | ||
| 
						 | 
					7542c6d49a | ||
| 
						 | 
					6571da922d | ||
| 
						 | 
					13b66cd215 | ||
| 
						 | 
					aca06c6007 | ||
| 
						 | 
					5184e08a5b | ||
| 
						 | 
					69c6d4bd26 | ||
| 
						 | 
					6a2bdd103b | ||
| 
						 | 
					76c4b96683 | ||
| 
						 | 
					c81e2dbb4a | ||
| 
						 | 
					44ed4e0e0d | ||
| 
						 | 
					b860db302c | ||
| 
						 | 
					e96d3e3738 | ||
| 
						 | 
					f1ca9ff15e | ||
| 
						 | 
					9eb23a62bc | ||
| 
						 | 
					9b6a839c06 | ||
| 
						 | 
					6aaa887e65 | ||
| 
						 | 
					1e332da11b | ||
| 
						 | 
					3a050cc5b8 | ||
| 
						 | 
					039d692e4c | ||
| 
						 | 
					ee608cc4a4 | ||
| 
						 | 
					d0183d44c9 | ||
| 
						 | 
					a7cc06a28b | ||
| 
						 | 
					da8eb334e3 | ||
| 
						 | 
					67592def90 | ||
| 
						 | 
					3484b315cf | ||
| 
						 | 
					b1e1e85bbd | ||
| 
						 | 
					1e79c507c8 | ||
| 
						 | 
					249a9b2eb2 | ||
| 
						 | 
					02abbdf4fe | ||
| 
						 | 
					08d68f99a5 | ||
| 
						 | 
					f27e4d6e06 | ||
| 
						 | 
					9c578c53db | ||
| 
						 | 
					a4a9acd2ea | ||
| 
						 | 
					c118234bbf | ||
| 
						 | 
					a94763041e | ||
| 
						 | 
					26892e7104 | ||
| 
						 | 
					5c74365f4b | ||
| 
						 | 
					94854e5965 | ||
| 
						 | 
					9cf30f4213 | ||
| 
						 | 
					eb776e69c0 | ||
| 
						 | 
					c4dd4f5c45 | ||
| 
						 | 
					33a63947e5 | ||
| 
						 | 
					432ec519b6 | ||
| 
						 | 
					7f65ba236c | ||
| 
						 | 
					3a86f5e746 | ||
| 
						 | 
					4e3e3653a3 | ||
| 
						 | 
					d3ea67fbd9 | ||
| 
						 | 
					9ebc04ef14 | ||
| 
						 | 
					e426e0e5ec | ||
| 
						 | 
					3d1e1659c2 | ||
| 
						 | 
					17f2bb966b | ||
| 
						 | 
					158f549df2 | ||
| 
						 | 
					a98b65286f | ||
| 
						 | 
					17aa666519 | ||
| 
						 | 
					f6cf4c3215 | ||
| 
						 | 
					86f0a9100f | ||
| 
						 | 
					f4b87cf70d | ||
| 
						 | 
					841273edf6 | ||
| 
						 | 
					366ec3d516 | ||
| 
						 | 
					a4e2aba6dc | ||
| 
						 | 
					f4ae86eb53 | ||
| 
						 | 
					3a932f5443 | ||
| 
						 | 
					ce58aff18a | ||
| 
						 | 
					24ee8ec063 | ||
| 
						 | 
					eee8790682 | ||
| 
						 | 
					083932e902 | ||
| 
						 | 
					9ab06cfdd2 | ||
| 
						 | 
					e159b7a15d | ||
| 
						 | 
					4481c44c4b | ||
| 
						 | 
					6d660d48ca | ||
| 
						 | 
					bf417a14e0 | ||
| 
						 | 
					4495c5b64a | ||
| 
						 | 
					0cee5ad380 | ||
| 
						 | 
					f22c0a7ee4 | ||
| 
						 | 
					83fce6ad77 | ||
| 
						 | 
					7b4934ec55 | ||
| 
						 | 
					2ef0c2a8cb | ||
| 
						 | 
					80c99d2eb6 | ||
| 
						 | 
					2ccca016a5 | ||
| 
						 | 
					cb0f72052f | ||
| 
						 | 
					1030eff943 | ||
| 
						 | 
					5e3caf6252 | ||
| 
						 | 
					aa67489404 | ||
| 
						 | 
					1ea6f7620e | ||
| 
						 | 
					365134b085 | ||
| 
						 | 
					07a2065c11 | ||
| 
						 | 
					f8cb3f464c | ||
| 
						 | 
					31337eaf4f | ||
| 
						 | 
					3e8ea972c2 | ||
| 
						 | 
					0f6287e715 | ||
| 
						 | 
					0cfd881510 | ||
| 
						 | 
					edca2a9f03 | ||
| 
						 | 
					cc05c50769 | ||
| 
						 | 
					adbb3486c5 | ||
| 
						 | 
					ced1609e51 | ||
| 
						 | 
					7fe866060b | ||
| 
						 | 
					7bc9b83aac | ||
| 
						 | 
					d6d16f8f1c | ||
| 
						 | 
					bcea3832ed | ||
| 
						 | 
					94662cb904 | ||
| 
						 | 
					26ab9af911 | ||
| 
						 | 
					f38b89a582 | ||
| 
						 | 
					c17ec19f97 | ||
| 
						 | 
					2ca7a175bd | ||
| 
						 | 
					371b690072 | ||
| 
						 | 
					c62e73f863 | ||
| 
						 | 
					5726dd0e0c | ||
| 
						 | 
					1c187c3914 | ||
| 
						 | 
					74cf4d73d5 | ||
| 
						 | 
					c8694f182a | ||
| 
						 | 
					2e767c94c4 | ||
| 
						 | 
					6302eee17e | ||
| 
						 | 
					820c15d74c | ||
| 
						 | 
					b26aa3cab7 | ||
| 
						 | 
					c51856522c | ||
| 
						 | 
					aefad6fdd3 | ||
| 
						 | 
					8371b1b25b | ||
| 
						 | 
					a9c85d5241 | ||
| 
						 | 
					edf52f32ad | ||
| 
						 | 
					18bc7d3637 | ||
| 
						 | 
					175490e1f7 | ||
| 
						 | 
					3492cd3942 | ||
| 
						 | 
					ed53808556 | ||
| 
						 | 
					41ddb76385 | ||
| 
						 | 
					94531a39d0 | ||
| 
						 | 
					55dd8cb1a8 | ||
| 
						 | 
					eefc746567 | ||
| 
						 | 
					0218bad9e8 | ||
| 
						 | 
					dd993c81a7 | ||
| 
						 | 
					3db62edf59 | ||
| 
						 | 
					914c823675 | ||
| 
						 | 
					a76e54483a | ||
| 
						 | 
					515470fa6a | ||
| 
						 | 
					cab675c8ca | ||
| 
						 | 
					deb9e3e2ee | ||
| 
						 | 
					3555f41f3e | ||
| 
						 | 
					b6b716b767 | ||
| 
						 | 
					7aea1647a2 | ||
| 
						 | 
					b74cbf1969 | ||
| 
						 | 
					eb10cbf87c | ||
| 
						 | 
					7918d7b287 | ||
| 
						 | 
					fde0dc2a35 | ||
| 
						 | 
					85f9d5a2da | ||
| 
						 | 
					ef7c1bd025 | ||
| 
						 | 
					9dd520f17b | ||
| 
						 | 
					a9e2d48036 | ||
| 
						 | 
					0520f1c70c | ||
| 
						 | 
					3b62f1a979 | ||
| 
						 | 
					d701567b70 | ||
| 
						 | 
					7911909c5f | ||
| 
						 | 
					b93d752c88 | ||
| 
						 | 
					5e4dcc1e35 | ||
| 
						 | 
					9939c809be | ||
| 
						 | 
					89cb6867bd | ||
| 
						 | 
					98801cb5d6 | ||
| 
						 | 
					48693df51f | ||
| 
						 | 
					23bc246a48 | ||
| 
						 | 
					11a45e4db0 | ||
| 
						 | 
					c450c22ddd | ||
| 
						 | 
					c19b3384ca | ||
| 
						 | 
					b7765ff388 | ||
| 
						 | 
					f377c7cb71 | ||
| 
						 | 
					aa08db1050 | ||
| 
						 | 
					a48a0820ad | ||
| 
						 | 
					109d933945 | ||
| 
						 | 
					7b471e5379 | ||
| 
						 | 
					de291ad3b1 | ||
| 
						 | 
					140d767026 | ||
| 
						 | 
					be5cad212a | ||
| 
						 | 
					efc42aa8f2 | ||
| 
						 | 
					f8a0ddb484 | ||
| 
						 | 
					3a0ba4f5a6 | ||
| 
						 | 
					a77920bd18 | ||
| 
						 | 
					78146d97f8 | ||
| 
						 | 
					2d5ec97dc3 | ||
| 
						 | 
					87aa0cfce2 | ||
| 
						 | 
					afddca510c | ||
| 
						 | 
					0b9b936368 | ||
| 
						 | 
					23f18c799d | ||
| 
						 | 
					a7a1ac70e5 | ||
| 
						 | 
					31eb366e7f | ||
| 
						 | 
					eaaa1a19ca | ||
| 
						 | 
					c08a460d30 | ||
| 
						 | 
					14f97eae9c | ||
| 
						 | 
					2f79323264 | ||
| 
						 | 
					1558c9d1bd | ||
| 
						 | 
					03aba8bbf7 | ||
| 
						 | 
					7141c2818a | ||
| 
						 | 
					058a0d393d | ||
| 
						 | 
					b0a23590d4 | ||
| 
						 | 
					48a9eef924 | ||
| 
						 | 
					62f335573a | ||
| 
						 | 
					c80e9bfa26 | ||
| 
						 | 
					ec62c27f36 | ||
| 
						 | 
					19b80ded16 | ||
| 
						 | 
					f72f88adfa | ||
| 
						 | 
					f083d019a3 | ||
| 
						 | 
					10ec30ba76 | ||
| 
						 | 
					3fb4296d23 | ||
| 
						 | 
					197ec0eb2c | ||
| 
						 | 
					1d162888dd | ||
| 
						 | 
					5ab26df4c9 | ||
| 
						 | 
					3783ed69d7 | ||
| 
						 | 
					1d2cd4745c | ||
| 
						 | 
					ee2d7efae2 | ||
| 
						 | 
					4a755e259f | ||
| 
						 | 
					4c56e4ffdc | ||
| 
						 | 
					7cad3ba0bc | ||
| 
						 | 
					449db4f3a9 | ||
| 
						 | 
					168a805e8e | ||
| 
						 | 
					7ded288154 | ||
| 
						 | 
					63a99f26f6 | ||
| 
						 | 
					7ca15a9de2 | ||
| 
						 | 
					7ac1d2458a | ||
| 
						 | 
					0bc4f2dc6e | ||
| 
						 | 
					6802484ad8 | ||
| 
						 | 
					dcd7d61c9a | ||
| 
						 | 
					3e94eba0d1 | ||
| 
						 | 
					ddae7823f9 | ||
| 
						 | 
					633c00f161 | ||
| 
						 | 
					be5253b9d3 | ||
| 
						 | 
					320be34798 | ||
| 
						 | 
					3dace28eb3 | ||
| 
						 | 
					19db1c9c2b | ||
| 
						 | 
					935360e2eb | ||
| 
						 | 
					e0a0d293fa | ||
| 
						 | 
					1a4d7618c1 | ||
| 
						 | 
					a2eabc1b08 | ||
| 
						 | 
					f76f6548a5 | ||
| 
						 | 
					44dce05ec5 | ||
| 
						 | 
					e729a966c7 | ||
| 
						 | 
					836c508698 | ||
| 
						 | 
					1fe492cc3d | ||
| 
						 | 
					8923f23f70 | ||
| 
						 | 
					4c8d07bba8 | ||
| 
						 | 
					37cd73c6e8 | ||
| 
						 | 
					096dce1fe0 | ||
| 
						 | 
					7c17581659 | ||
| 
						 | 
					0a39984c4f | ||
| 
						 | 
					ae763d000c | ||
| 
						 | 
					a146185bd2 | ||
| 
						 | 
					0b635afcf7 | ||
| 
						 | 
					6407a66d30 | ||
| 
						 | 
					4533262b4a | ||
| 
						 | 
					6a0bd10d5b | ||
| 
						 | 
					3f2a23c93b | ||
| 
						 | 
					96f738b6b6 | ||
| 
						 | 
					f46a0853b9 | ||
| 
						 | 
					3c325b8870 | ||
| 
						 | 
					7f486f739d | ||
| 
						 | 
					66c81ce3d6 | ||
| 
						 | 
					5fa6db72d2 | ||
| 
						 | 
					6b65ae77e7 | ||
| 
						 | 
					e43e14ec00 | ||
| 
						 | 
					c9acfc9f89 | ||
| 
						 | 
					0bf510ca08 | ||
| 
						 | 
					fc0c902886 | ||
| 
						 | 
					f8dd69bbfa | ||
| 
						 | 
					926ee14546 | ||
| 
						 | 
					66408eeec8 | ||
| 
						 | 
					aea286779e | ||
| 
						 | 
					55d52d060a | ||
| 
						 | 
					d23f4de36a | ||
| 
						 | 
					503bae661c | ||
| 
						 | 
					d82be0deef | ||
| 
						 | 
					eab140df48 | ||
| 
						 | 
					15e39e00c2 | ||
| 
						 | 
					077a0ce3e3 | ||
| 
						 | 
					5711b2b241 | ||
| 
						 | 
					d9a1b9d530 | ||
| 
						 | 
					b6c1eadb91 | ||
| 
						 | 
					56265c2277 | ||
| 
						 | 
					9ea1d24121 | ||
| 
						 | 
					40e85b718d | ||
| 
						 | 
					bbce5c3154 | ||
| 
						 | 
					c869aaf91a | ||
| 
						 | 
					5b0bf0a0ba | ||
| 
						 | 
					ec87ac7652 | ||
| 
						 | 
					8f65c73c8a | ||
| 
						 | 
					35316dc31c | ||
| 
						 | 
					b6bb689ccc | ||
| 
						 | 
					3d524d7572 | ||
| 
						 | 
					45bedb20c1 | ||
| 
						 | 
					2ca3c6ea93 | ||
| 
						 | 
					9b922913a0 | ||
| 
						 | 
					4803eef907 | ||
| 
						 | 
					4240e8843b | ||
| 
						 | 
					4439e1680d | ||
| 
						 | 
					5569ef8b2d | ||
| 
						 | 
					e05f16d780 | ||
| 
						 | 
					ae7fc27c9e | ||
| 
						 | 
					ff5e7486bc | ||
| 
						 | 
					867b5f726a | ||
| 
						 | 
					a4c704a5a2 | ||
| 
						 | 
					e928e02b1c | ||
| 
						 | 
					1cd6685b36 | ||
| 
						 | 
					695582b30f | ||
| 
						 | 
					d0bad46627 | ||
| 
						 | 
					eb0bdba666 | ||
| 
						 | 
					19b9668190 | ||
| 
						 | 
					59d4c362f6 | ||
| 
						 | 
					9be646f1e7 | ||
| 
						 | 
					164d5485e9 | ||
| 
						 | 
					7678289ead | ||
| 
						 | 
					2a62494296 | ||
| 
						 | 
					ae94ba73d7 | ||
| 
						 | 
					560a2e035e | ||
| 
						 | 
					5b3f78287e | ||
| 
						 | 
					4bb000dd93 | ||
| 
						 | 
					06d9b68406 | ||
| 
						 | 
					eca52a8ca6 | ||
| 
						 | 
					64e2de7aa6 | ||
| 
						 | 
					3a3e98cc48 | ||
| 
						 | 
					ee64ef5fc1 | ||
| 
						 | 
					6e11941ae9 | ||
| 
						 | 
					ad482600de | ||
| 
						 | 
					7c89e4aa4a | ||
| 
						 | 
					f8471bc944 | ||
| 
						 | 
					15b7e3ac9b | ||
| 
						 | 
					2983ebb2c6 | ||
| 
						 | 
					25e8bb44d2 | ||
| 
						 | 
					51bc7c6a7f | ||
| 
						 | 
					60dad014cc | ||
| 
						 | 
					1a8eece90f | ||
| 
						 | 
					85658d3d1f | ||
| 
						 | 
					a7cd6d8807 | ||
| 
						 | 
					ddce0f371d | ||
| 
						 | 
					495cd06ed5 | ||
| 
						 | 
					1624866656 | ||
| 
						 | 
					bce56c53ed | ||
| 
						 | 
					0b3170d2ef | ||
| 
						 | 
					2d1aa65d63 | ||
| 
						 | 
					7e7754f9aa | ||
| 
						 | 
					3a96a246a4 | ||
| 
						 | 
					24682c5f7a | ||
| 
						 | 
					28365602f0 | ||
| 
						 | 
					2c5da44e5e | ||
| 
						 | 
					a03046400c | ||
| 
						 | 
					d5c835e777 | ||
| 
						 | 
					af630d1b3e | ||
| 
						 | 
					c333a2e70a | ||
| 
						 | 
					6b613f234b | ||
| 
						 | 
					1619f236cf | ||
| 
						 | 
					906354764b | ||
| 
						 | 
					12ec293f3d | ||
| 
						 | 
					bdf8b4d3f1 | ||
| 
						 | 
					5ee8dd239b | ||
| 
						 | 
					6e60088b11 | ||
| 
						 | 
					97bf530b1d | ||
| 
						 | 
					18348476c6 | ||
| 
						 | 
					7e35e55f0b | ||
| 
						 | 
					f2cd55d6cd | ||
| 
						 | 
					28a2a5b92a | ||
| 
						 | 
					639cf1cf59 | ||
| 
						 | 
					d262520594 | ||
| 
						 | 
					7cc14a195b | ||
| 
						 | 
					84b9e4d781 | ||
| 
						 | 
					ce433a01b2 | ||
| 
						 | 
					495d8c9830 | ||
| 
						 | 
					52e0476d24 | ||
| 
						 | 
					86340623f7 | ||
| 
						 | 
					356fa73c91 | ||
| 
						 | 
					a073ef69ac | ||
| 
						 | 
					3c6c420023 | ||
| 
						 | 
					2ffe1cdff9 | ||
| 
						 | 
					32b58715c6 | ||
| 
						 | 
					1a9dd2f144 | ||
| 
						 | 
					21964fe68e | ||
| 
						 | 
					62714bb97c | ||
| 
						 | 
					2a4943f584 | ||
| 
						 | 
					e32403a75f | ||
| 
						 | 
					d6a7dc9820 | ||
| 
						 | 
					dc53cf60d1 | ||
| 
						 | 
					e0e5ad1347 | ||
| 
						 | 
					a6b11354cd | ||
| 
						 | 
					f7c0cb6214 | ||
| 
						 | 
					561d671181 | ||
| 
						 | 
					d569b13101 | ||
| 
						 | 
					b0c42c12b7 | ||
| 
						 | 
					621ccc25ec | ||
| 
						 | 
					617df739ee | ||
| 
						 | 
					6e43a92191 | ||
| 
						 | 
					84486f2086 | ||
| 
						 | 
					dc79f460e5 | ||
| 
						 | 
					7cac858697 | ||
| 
						 | 
					a3977a18f0 | ||
| 
						 | 
					5e858f73d5 | ||
| 
						 | 
					a9418fd2c7 | ||
| 
						 | 
					1d7a0fa48d | ||
| 
						 | 
					30c530178a | ||
| 
						 | 
					3d0795cde3 | ||
| 
						 | 
					181fb32a2a | ||
| 
						 | 
					232a648fbd | ||
| 
						 | 
					d35a149108 | ||
| 
						 | 
					c85a3787c0 | ||
| 
						 | 
					066258ab83 | ||
| 
						 | 
					ddc2295ec3 | ||
| 
						 | 
					011e6fc512 | ||
| 
						 | 
					dca0014862 | ||
| 
						 | 
					6b02295bf4 | ||
| 
						 | 
					05c43db6fe | ||
| 
						 | 
					9143f5febd | ||
| 
						 | 
					91bd58215a | ||
| 
						 | 
					b37ee19fbc | ||
| 
						 | 
					2355d354b3 | ||
| 
						 | 
					300280e03e | ||
| 
						 | 
					200c6e49fc | ||
| 
						 | 
					a89f9eed42 | ||
| 
						 | 
					341218e8f6 | ||
| 
						 | 
					33e8084852 | ||
| 
						 | 
					7d495f6389 | ||
| 
						 | 
					f4d34a2a9c | ||
| 
						 | 
					0feab1965c | ||
| 
						 | 
					7993e4c03f | ||
| 
						 | 
					b592b89dc8 | ||
| 
						 | 
					d115166dde | ||
| 
						 | 
					d176f9f811 | ||
| 
						 | 
					aa17d3034e | ||
| 
						 | 
					52a9fcf5d6 | ||
| 
						 | 
					143e3eeb6c | ||
| 
						 | 
					7b271dcb6b | ||
| 
						 | 
					2680e466fa | ||
| 
						 | 
					29d5535e7c | ||
| 
						 | 
					ebb5474b34 | ||
| 
						 | 
					4759162bb7 | ||
| 
						 | 
					deba184103 | ||
| 
						 | 
					c65a278fcf | ||
| 
						 | 
					50ce800c2a | ||
| 
						 | 
					1a1eecb4c3 | ||
| 
						 | 
					b3bc8b6876 | ||
| 
						 | 
					8c08f7b93e | ||
| 
						 | 
					501e426868 | ||
| 
						 | 
					8f1f4eb177 | ||
| 
						 | 
					b0e7dca985 | ||
| 
						 | 
					b16af5fe3e | ||
| 
						 | 
					1f7ba70ad7 | ||
| 
						 | 
					1dbe039be8 | ||
| 
						 | 
					d82538a799 | ||
| 
						 | 
					57d45b24be | ||
| 
						 | 
					dcd4006017 | ||
| 
						 | 
					7aba3b6672 | ||
| 
						 | 
					3ae681c79c | ||
| 
						 | 
					ab3d3ec45b | ||
| 
						 | 
					ff3770c9ee | ||
| 
						 | 
					bcc1e2e26f | ||
| 
						 | 
					926b3d075c | ||
| 
						 | 
					b5f8e6d90c | ||
| 
						 | 
					827cb27f28 | ||
| 
						 | 
					739fa423c5 | ||
| 
						 | 
					5f30249065 | ||
| 
						 | 
					4191bc1ac3 | ||
| 
						 | 
					92df11acc1 | ||
| 
						 | 
					416bd82589 | ||
| 
						 | 
					7934e8d425 | ||
| 
						 | 
					0842f464db | ||
| 
						 | 
					5a93e5a2bc | ||
| 
						 | 
					56e51ea32a | ||
| 
						 | 
					41c38f202d | ||
| 
						 | 
					f0f396f656 | ||
| 
						 | 
					a5febf8f0f | ||
| 
						 | 
					4f4af87285 | ||
| 
						 | 
					1b5fbccc06 | ||
| 
						 | 
					fc123cd367 | ||
| 
						 | 
					a0f4b98eed | ||
| 
						 | 
					7adcfc221a | ||
| 
						 | 
					c1dcd8ea5b | ||
| 
						 | 
					74961e1106 | ||
| 
						 | 
					c79d1f2648 | ||
| 
						 | 
					f4bd7d7a44 | ||
| 
						 | 
					1891b24bde | ||
| 
						 | 
					5520037908 | ||
| 
						 | 
					c0c0371451 | ||
| 
						 | 
					ed956d351d | ||
| 
						 | 
					e9e9ca146b | ||
| 
						 | 
					dcdafbaebf | ||
| 
						 | 
					f98915cddd | ||
| 
						 | 
					4e6c307684 | ||
| 
						 | 
					ce6d74390e | ||
| 
						 | 
					6e406fd060 | ||
| 
						 | 
					2614427e0e | ||
| 
						 | 
					e8b662571b | ||
| 
						 | 
					2a784115d5 | ||
| 
						 | 
					9f5f14826b | ||
| 
						 | 
					c75d94289b | ||
| 
						 | 
					272c6bca97 | ||
| 
						 | 
					0d7387080d | ||
| 
						 | 
					492dbcbc51 | ||
| 
						 | 
					1e2e20b145 | ||
| 
						 | 
					c82163fa7b | ||
| 
						 | 
					cf3169dd68 | ||
| 
						 | 
					c1ebd50e4c | ||
| 
						 | 
					488829a20c | ||
| 
						 | 
					85c7a36e25 | ||
| 
						 | 
					442a1979e7 | ||
| 
						 | 
					d99b4d75d8 | ||
| 
						 | 
					922a724e36 | ||
| 
						 | 
					15a88967d0 | ||
| 
						 | 
					15a9d2380c | ||
| 
						 | 
					80e0bd875a | ||
| 
						 | 
					6e6fbe0d65 | ||
| 
						 | 
					180e5f14ae | ||
| 
						 | 
					e9314898d2 | ||
| 
						 | 
					bc2072b8c6 | ||
| 
						 | 
					25208a9074 | ||
| 
						 | 
					ce75d19480 | ||
| 
						 | 
					cd3c2312a5 | ||
| 
						 | 
					6f2ad0dadc | ||
| 
						 | 
					1e05ebbfd7 | ||
| 
						 | 
					c5c3546b92 | ||
| 
						 | 
					a9dd1f9828 | ||
| 
						 | 
					4b5540830b | ||
| 
						 | 
					672fa74321 | ||
| 
						 | 
					3fcf061679 | ||
| 
						 | 
					ee35976c92 | ||
| 
						 | 
					b94ef695e2 | ||
| 
						 | 
					54b6cd1100 | ||
| 
						 | 
					759eaa3b35 | ||
| 
						 | 
					eee9309650 | ||
| 
						 | 
					e634d77a8e | ||
| 
						 | 
					06b26b799e | ||
| 
						 | 
					d6720884f1 | ||
| 
						 | 
					eb5946c1c1 | ||
| 
						 | 
					227cb42ba7 | ||
| 
						 | 
					3420eb69d3 | ||
| 
						 | 
					6b3088f241 | ||
| 
						 | 
					fdcc6432f1 | ||
| 
						 | 
					f8e028a5d1 | ||
| 
						 | 
					5e6f1214fb | ||
| 
						 | 
					0371e2e314 | ||
| 
						 | 
					0f89e98a71 | ||
| 
						 | 
					6f07a21bb8 | ||
| 
						 | 
					dcb5af87a7 | ||
| 
						 | 
					f3678d9d52 | ||
| 
						 | 
					35d038e826 | ||
| 
						 | 
					27c7410318 | ||
| 
						 | 
					7c627fae8f | ||
| 
						 | 
					bf0e6692f5 | ||
| 
						 | 
					1d0d2302d8 | ||
| 
						 | 
					1a8fd38430 | ||
| 
						 | 
					503811f69c | ||
| 
						 | 
					5892594668 | ||
| 
						 | 
					b9f8f6e33d | ||
| 
						 | 
					457193657f | ||
| 
						 | 
					c92d15ba42 | ||
| 
						 | 
					d7dd53c2da | ||
| 
						 | 
					b35fddefc0 | ||
| 
						 | 
					07e08c283a | ||
| 
						 | 
					89d0ea6506 | ||
| 
						 | 
					75ce5cd0e9 | ||
| 
						 | 
					9a7bbd92bd | ||
| 
						 | 
					b994b2a862 | ||
| 
						 | 
					3cb0b41b22 | ||
| 
						 | 
					edcafc5835 | ||
| 
						 | 
					7d09df9a85 | ||
| 
						 | 
					b00eee00fc | ||
| 
						 | 
					c0d83f9395 | ||
| 
						 | 
					be757066f5 | ||
| 
						 | 
					5980575918 | ||
| 
						 | 
					409d12aa75 | ||
| 
						 | 
					885433390e | ||
| 
						 | 
					687f810475 | ||
| 
						 | 
					137a60f510 | ||
| 
						 | 
					2432e9c2aa | ||
| 
						 | 
					46d5a1431f | ||
| 
						 | 
					404d02ec23 | ||
| 
						 | 
					03c0678cbe | ||
| 
						 | 
					04257aedae | ||
| 
						 | 
					33b30f7583 | ||
| 
						 | 
					8eda495aa7 | ||
| 
						 | 
					a337e04fae | ||
| 
						 | 
					2e7f23a766 | ||
| 
						 | 
					6d1cb85a07 | ||
| 
						 | 
					b5756d2b99 | ||
| 
						 | 
					eec955317a | ||
| 
						 | 
					efba0f0236 | ||
| 
						 | 
					67890a7298 | ||
| 
						 | 
					dd457f26c6 | ||
| 
						 | 
					85c6bda5c9 | ||
| 
						 | 
					b7b5f87002 | ||
| 
						 | 
					b0c5d807e7 | ||
| 
						 | 
					4d4776e0ef | ||
| 
						 | 
					61e1aeb1d8 | ||
| 
						 | 
					9caa603a65 | ||
| 
						 | 
					5c99e469d5 | ||
| 
						 | 
					4d9dc3624b | ||
| 
						 | 
					d3ae4b729f | ||
| 
						 | 
					4e39c4f900 | ||
| 
						 | 
					30bed434fe | ||
| 
						 | 
					3e4ae4d38d | ||
| 
						 | 
					7414275a85 | ||
| 
						 | 
					fcb0033864 | ||
| 
						 | 
					f079471a18 | ||
| 
						 | 
					1fb1174c0a | ||
| 
						 | 
					635e1eda69 | ||
| 
						 | 
					40d53f941f | ||
| 
						 | 
					e339ee1dd1 | ||
| 
						 | 
					684a0e88a2 | ||
| 
						 | 
					30a4f15330 | ||
| 
						 | 
					dfd08000f1 | ||
| 
						 | 
					94b306e129 | ||
| 
						 | 
					82c8d26264 | ||
| 
						 | 
					bd236e1cb1 | ||
| 
						 | 
					e47a36e799 | ||
| 
						 | 
					30efec0090 | ||
| 
						 | 
					b9371ea03d | ||
| 
						 | 
					7974f33781 | ||
| 
						 | 
					32eaf3893a | ||
| 
						 | 
					cdcaedc8dd | ||
| 
						 | 
					845b9a2faa | ||
| 
						 | 
					5870248617 | ||
| 
						 | 
					07ef4dfe8a | ||
| 
						 | 
					342be0822f | ||
| 
						 | 
					5c56484889 | ||
| 
						 | 
					2b4162c0be | ||
| 
						 | 
					3704d64560 | ||
| 
						 | 
					24fae72492 | ||
| 
						 | 
					79fb6eabd9 | ||
| 
						 | 
					1ddb00c8d6 | ||
| 
						 | 
					8a0b77ec5c | ||
| 
						 | 
					2fc447e16f | ||
| 
						 | 
					d828bf59f9 | ||
| 
						 | 
					d8806f14aa | ||
| 
						 | 
					0c6a9ca857 | ||
| 
						 | 
					07c907b315 | ||
| 
						 | 
					d343d38037 | ||
| 
						 | 
					7e89c13839 | ||
| 
						 | 
					2a032bf66d | ||
| 
						 | 
					e0727e8485 | ||
| 
						 | 
					d5d7ac90ac | ||
| 
						 | 
					3e5fa30d9d | ||
| 
						 | 
					73241efdc8 | ||
| 
						 | 
					cc470f671a | ||
| 
						 | 
					933927cda1 | 
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,8 +3,13 @@
 | 
			
		||||
*.gzip
 | 
			
		||||
*.tgz
 | 
			
		||||
*.DS_Store
 | 
			
		||||
*.idea
 | 
			
		||||
 | 
			
		||||
*.sass-cache
 | 
			
		||||
*COMPILE.css
 | 
			
		||||
 | 
			
		||||
# Intellij project configuration files
 | 
			
		||||
*.idea
 | 
			
		||||
*.iml
 | 
			
		||||
 | 
			
		||||
# External dependencies
 | 
			
		||||
 | 
			
		||||
@@ -20,5 +25,6 @@ closed-lib
 | 
			
		||||
# Node dependencies
 | 
			
		||||
node_modules
 | 
			
		||||
 | 
			
		||||
# Build documentation
 | 
			
		||||
docs
 | 
			
		||||
# Protractor logs
 | 
			
		||||
protractor/logs
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										301
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,301 @@
 | 
			
		||||
# Contributing to Open MCT Web
 | 
			
		||||
 | 
			
		||||
This document describes the process of contributing to Open MCT Web as well
 | 
			
		||||
as the standards that will be applied when evaluating contributions.
 | 
			
		||||
 | 
			
		||||
Please be aware that additional agreements will be necessary before we can
 | 
			
		||||
accept changes from external contributors.
 | 
			
		||||
 | 
			
		||||
## Summary
 | 
			
		||||
 | 
			
		||||
The short version:
 | 
			
		||||
 | 
			
		||||
1. Write your contribution.
 | 
			
		||||
2. Make sure your contribution meets code, test, and commit message
 | 
			
		||||
   standards as described below.
 | 
			
		||||
3. Submit a pull request from a topic branch back to `master`. Include a check
 | 
			
		||||
   list, as described below. (Optionally, assign this to a specific member
 | 
			
		||||
   for review.)
 | 
			
		||||
4. Respond to any discussion. When the reviewer decides it's ready, they
 | 
			
		||||
   will merge back `master` and fill out their own check list.
 | 
			
		||||
 | 
			
		||||
## Contribution Process
 | 
			
		||||
 | 
			
		||||
Open MCT Web uses git for software version control, and for branching and
 | 
			
		||||
merging. The central repository is at
 | 
			
		||||
https://github.com/nasa/openmctweb.git.
 | 
			
		||||
 | 
			
		||||
### Roles
 | 
			
		||||
 | 
			
		||||
References to roles are made throughout this document. These are not intended
 | 
			
		||||
to reflect titles or long-term job assignments; rather, these are used as
 | 
			
		||||
descriptors to refer to members of the development team performing tasks in
 | 
			
		||||
the check-in process. These roles are:
 | 
			
		||||
 | 
			
		||||
* _Author_: The individual who has made changes to files in the software
 | 
			
		||||
  repository, and wishes to check these in.
 | 
			
		||||
* _Reviewer_: The individual who reviews changes to files before they are
 | 
			
		||||
  checked in.
 | 
			
		||||
* _Integrator_: The individual who performs the task of merging these files.
 | 
			
		||||
  Usually the reviewer.
 | 
			
		||||
 | 
			
		||||
### Branching
 | 
			
		||||
 | 
			
		||||
Three basic types of branches may be included in the above repository:
 | 
			
		||||
 | 
			
		||||
1. Master branch.
 | 
			
		||||
2. Topic branches.
 | 
			
		||||
3. Developer branches.
 | 
			
		||||
 | 
			
		||||
Branches which do not fit into the above categories may be created and used
 | 
			
		||||
during the course of development for various reasons, such as large-scale
 | 
			
		||||
refactoring of code or implementation of complex features which may cause
 | 
			
		||||
instability. In these exceptional cases it is the responsibility of the
 | 
			
		||||
developer who initiates the task which motivated this branching to
 | 
			
		||||
communicate to the team the role of these branches and any associated
 | 
			
		||||
procedures for the duration of their use.
 | 
			
		||||
 | 
			
		||||
#### Master Branch
 | 
			
		||||
 | 
			
		||||
The role of the `master` branches is to represent the latest
 | 
			
		||||
"ready for test" version of the software. Source code on the master
 | 
			
		||||
branch has undergone peer review, and will undergo regular automated
 | 
			
		||||
testing with notification on failure. Master branches may be unstable
 | 
			
		||||
(particularly for recent features), but the intent is for the stability of
 | 
			
		||||
any features on master branches to be non-decreasing. It is the shared
 | 
			
		||||
responsibility of authors, reviewers, and integrators to ensure this.
 | 
			
		||||
 | 
			
		||||
#### Topic Branches
 | 
			
		||||
 | 
			
		||||
Topic branches are used by developers to perform and record work on issues.
 | 
			
		||||
 | 
			
		||||
Topic branches need not necessarily be stable, even when pushed to the
 | 
			
		||||
central repository; in fact, the practice of making incremental commits
 | 
			
		||||
while working on an issue and pushing these to the central repository is
 | 
			
		||||
encouraged, to avoid lost work and to share work-in-progress. (Small commits
 | 
			
		||||
also help isolate changes, which can help in identifying which change
 | 
			
		||||
introduced a defect, particularly when that defect went unnoticed for some
 | 
			
		||||
time, e.g. using `git bisect`.)
 | 
			
		||||
 | 
			
		||||
Topic branches should be named according to their corresponding issue
 | 
			
		||||
identifiers, all lower case, without hyphens. (e.g. branch mct9 would refer
 | 
			
		||||
to issue #9.)
 | 
			
		||||
 | 
			
		||||
In some cases, work on an issue may warrant the use of multiple divergent
 | 
			
		||||
branches; for instance, when a developer wants to try more than one solution
 | 
			
		||||
and compare them, or when a "dead end" is reached and an initial approach to
 | 
			
		||||
resolving an issue needs to be abandoned. In these cases, a short suffix
 | 
			
		||||
should be added to the additional branches; this may be simply a single
 | 
			
		||||
character (e.g. wtd481b) or, where useful, a descriptive term for what
 | 
			
		||||
distinguishes the branches (e.g. wtd481verbose). It is the responsibility of
 | 
			
		||||
the author to communicate which branch is intended to be merged to both the
 | 
			
		||||
reviewer and the integrator.
 | 
			
		||||
 | 
			
		||||
#### Developer Branches
 | 
			
		||||
 | 
			
		||||
Developer branches are any branches used for purposes outside of the scope
 | 
			
		||||
of the above; e.g. to try things out, or maintain a "my latest stuff"
 | 
			
		||||
branch that is not delayed by the review and integration process. These
 | 
			
		||||
may be pushed to the central repository, and may follow any naming convention
 | 
			
		||||
desired so long as the owner of the branch is identifiable, and so long as
 | 
			
		||||
the name chosen could not be mistaken for a topic or master branch.
 | 
			
		||||
 | 
			
		||||
### Merging
 | 
			
		||||
 | 
			
		||||
When development is complete on an issue, the first step toward merging it
 | 
			
		||||
back into the master branch is to file a Pull Request. The contributions
 | 
			
		||||
should meet code, test, and commit message standards as described below,
 | 
			
		||||
and the pull request should include a completed author checklist, also
 | 
			
		||||
as described below. Pull requests may be assigned to specific team
 | 
			
		||||
members when appropriate (e.g. to draw to a specific person's attention.)
 | 
			
		||||
 | 
			
		||||
Code review should take place using discussion features within the pull
 | 
			
		||||
request. When the reviewer is satisfied, they should add a comment to
 | 
			
		||||
the pull request containing the reviewer checklist (from below) and complete
 | 
			
		||||
the merge back to the master branch.
 | 
			
		||||
 | 
			
		||||
## Standards
 | 
			
		||||
 | 
			
		||||
Contributions to Open MCT Web are expected to meet the following standards.
 | 
			
		||||
In addition, reviewers should use general discretion before accepting
 | 
			
		||||
changes.
 | 
			
		||||
 | 
			
		||||
### Code Standards
 | 
			
		||||
 | 
			
		||||
JavaScript sources in Open MCT Web must satisfy JSLint under its default
 | 
			
		||||
settings. This is verified by the command line build.
 | 
			
		||||
 | 
			
		||||
#### Code Guidelines
 | 
			
		||||
 | 
			
		||||
JavaScript sources in Open MCT Web should:
 | 
			
		||||
 | 
			
		||||
* Use four spaces for indentation. Tabs should not be used.
 | 
			
		||||
* Include JSDoc for any exposed API (e.g. public methods, constructors.)
 | 
			
		||||
* Include non-JSDoc comments as-needed for explaining private variables,
 | 
			
		||||
  methods, or algorithms when they are non-obvious.
 | 
			
		||||
* Define one public class per script, expressed as a constructor function
 | 
			
		||||
  returned from an AMD-style module.
 | 
			
		||||
* Follow “Java-like” naming conventions. These includes:
 | 
			
		||||
  * Classes should use camel case, first letter capitalized
 | 
			
		||||
    (e.g. SomeClassName.)
 | 
			
		||||
  * Methods, variables, fields, and function names should use camel case,
 | 
			
		||||
    first letter lower-case (e.g. someVariableName.) Constants
 | 
			
		||||
    (variables or fields which are meant to be declared and initialized
 | 
			
		||||
    statically, and never changed) should use only capital letters, with
 | 
			
		||||
    underscores between words (e.g. SOME_CONSTANT.)
 | 
			
		||||
  * File name should be the name of the exported class, plus a .js extension
 | 
			
		||||
    (e.g. SomeClassName.js)
 | 
			
		||||
* Avoid anonymous functions, except when functions are short (a few lines)
 | 
			
		||||
  and/or their inclusion makes sense within the flow of the code
 | 
			
		||||
  (e.g. as arguments to a forEach call.)
 | 
			
		||||
* Avoid deep nesting (especially of functions), except where necessary
 | 
			
		||||
  (e.g. due to closure scope.)
 | 
			
		||||
* End with a single new-line character.
 | 
			
		||||
* Expose public methods by declaring them on the class's prototype.
 | 
			
		||||
* Within a given function's scope, do not mix declarations and imperative
 | 
			
		||||
  code, and  present these in the following order:
 | 
			
		||||
  * First, variable declarations and initialization.
 | 
			
		||||
  * Second, function declarations.
 | 
			
		||||
  * Third, imperative statements.
 | 
			
		||||
  * Finally, the returned value.
 | 
			
		||||
 | 
			
		||||
Deviations from Open MCT Web code style guidelines require two-party agreement,
 | 
			
		||||
typically from the author of the change and its reviewer.
 | 
			
		||||
 | 
			
		||||
#### Code Example
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bundles should declare themselves as namespaces in whichever source
 | 
			
		||||
 * file is most like the "main point of entry" to the bundle.
 | 
			
		||||
 * @namespace some/bundle
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ['./OtherClass'],
 | 
			
		||||
    function (OtherClass) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A summary of how to use this class goes here.
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof some/bundle
 | 
			
		||||
         */
 | 
			
		||||
        function ExampleClass() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Methods which are not intended for external use should
 | 
			
		||||
        // not have JSDoc (or should be marked @private)
 | 
			
		||||
        ExampleClass.prototype.privateMethod = function () {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A summary of this method goes here.
 | 
			
		||||
         * @param {number} n a parameter
 | 
			
		||||
         * @returns {number} a return value
 | 
			
		||||
         */
 | 
			
		||||
        ExampleClass.prototype.publicMethod = function (n) {
 | 
			
		||||
            return n * 2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ExampleClass;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Test Standards
 | 
			
		||||
 | 
			
		||||
Automated testing shall occur whenever changes are merged into the main
 | 
			
		||||
development branch and must be confirmed alongside any pull request.
 | 
			
		||||
 | 
			
		||||
Automated tests are typically unit tests which exercise individual software
 | 
			
		||||
components. Tests are subject to code review along with the actual
 | 
			
		||||
implementation, to ensure that tests are applicable and useful.
 | 
			
		||||
 | 
			
		||||
Examples of useful tests:
 | 
			
		||||
* Tests which replicate bugs (or their root causes) to verify their
 | 
			
		||||
  resolution.
 | 
			
		||||
* Tests which reflect details from software specifications.
 | 
			
		||||
* Tests which exercise edge or corner cases among inputs.
 | 
			
		||||
* Tests which verify expected interactions with other components in the
 | 
			
		||||
  system.
 | 
			
		||||
 | 
			
		||||
During automated testing, code coverage metrics will be reported. Line
 | 
			
		||||
coverage must remain at or above 80%.
 | 
			
		||||
 | 
			
		||||
### Commit Message Standards
 | 
			
		||||
 | 
			
		||||
Commit messages should:
 | 
			
		||||
 | 
			
		||||
* Contain a one-line subject, followed by one line of white space,
 | 
			
		||||
  followed by one or more descriptive paragraphs, each separated by one
 | 
			
		||||
  line of white space.
 | 
			
		||||
* Contain a short (usually one word) reference to the feature or subsystem
 | 
			
		||||
  the commit effects, in square brackets, at the start of the subject line
 | 
			
		||||
  (e.g. `[Documentation] Draft of check-in process`)
 | 
			
		||||
* Contain a reference to a relevant issue number in the body of the commit.
 | 
			
		||||
  * This is important for traceability; while branch names also provide this,
 | 
			
		||||
    you cannot tell from looking at a commit what branch it was authored on.
 | 
			
		||||
  * This may be omitted if the relevant issue is otherwise obvious from the
 | 
			
		||||
    commit history (that is, if using `git log` from the relevant commit
 | 
			
		||||
    directly leads to a similar issue reference) to minimize clutter.
 | 
			
		||||
* Describe the change that was made, and any useful rationale therefore.
 | 
			
		||||
  * Comments in code should explain what things do, commit messages describe
 | 
			
		||||
    how they came to be done that way.
 | 
			
		||||
* Provide sufficient information for a reviewer to understand the changes
 | 
			
		||||
  made and their relationship to previous code.
 | 
			
		||||
 | 
			
		||||
Commit messages should not:
 | 
			
		||||
 | 
			
		||||
* Exceed 54 characters in length on the subject line.
 | 
			
		||||
* Exceed 72 characters in length in the body of the commit.
 | 
			
		||||
  * Except where necessary to maintain the structure of machine-readable or
 | 
			
		||||
    machine-generated text (e.g. error messages)
 | 
			
		||||
 | 
			
		||||
See [Contributing to a Project](http://git-scm.com/book/ch5-2.html) from
 | 
			
		||||
Pro Git by Shawn Chacon and Ben Straub for a bit of the rationale behind
 | 
			
		||||
these standards.
 | 
			
		||||
 | 
			
		||||
## Issue Reporting
 | 
			
		||||
 | 
			
		||||
Issues are tracked at https://github.com/nasa/openmctweb/issues
 | 
			
		||||
 | 
			
		||||
Issues should include:
 | 
			
		||||
 | 
			
		||||
* A short description of the issue encountered.
 | 
			
		||||
* A longer-form description of the issue encountered. When possible, steps to
 | 
			
		||||
  reproduce the issue.
 | 
			
		||||
* When possible, a description of the impact of the issue. What use case does
 | 
			
		||||
  it impede?
 | 
			
		||||
* An assessment of the severity of the issue.
 | 
			
		||||
 | 
			
		||||
Issue severity is categorized as follows (in ascending order):
 | 
			
		||||
 | 
			
		||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
 | 
			
		||||
  software; a "nice-to-have."
 | 
			
		||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
 | 
			
		||||
* _Critical_: Large-scale loss of functionality or impairment of use,
 | 
			
		||||
  such that remaining utility becomes marginal.
 | 
			
		||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
 | 
			
		||||
 | 
			
		||||
## Check Lists
 | 
			
		||||
 | 
			
		||||
The following check lists should be completed and attached to pull requests
 | 
			
		||||
when they are filed (author checklist) and when they are merged (reviewer
 | 
			
		||||
checklist.)
 | 
			
		||||
 | 
			
		||||
### Author Checklist
 | 
			
		||||
 | 
			
		||||
1. Changes address original issue?
 | 
			
		||||
2. Unit tests included and/or updated with changes?
 | 
			
		||||
3. Command line build passes?
 | 
			
		||||
4. Expect to pass code review?
 | 
			
		||||
 | 
			
		||||
### Reviewer Checklist
 | 
			
		||||
 | 
			
		||||
1. Changes appear to address issue?
 | 
			
		||||
2. Appropriate unit tests included?
 | 
			
		||||
3. Code style and in-line documentation are appropriate?
 | 
			
		||||
4. Commit messages meet standards?
 | 
			
		||||
							
								
								
									
										28
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								README.md
									
									
									
									
									
								
							@@ -67,6 +67,19 @@ as described above.
 | 
			
		||||
An example of this is expressed in `platform/framework`, which follows
 | 
			
		||||
bundle conventions.
 | 
			
		||||
 | 
			
		||||
### Functional Testing
 | 
			
		||||
 | 
			
		||||
The tests described above are all at the unit-level; an additional
 | 
			
		||||
test suite using [Protractor](https://angular.github.io/protractor/)
 | 
			
		||||
us under development, in the `protractor` folder.
 | 
			
		||||
 | 
			
		||||
To run:
 | 
			
		||||
 | 
			
		||||
* Install protractor following the instructions above.
 | 
			
		||||
* `cd protractor`
 | 
			
		||||
* `npm install`
 | 
			
		||||
* `npm run all`
 | 
			
		||||
 | 
			
		||||
## Build
 | 
			
		||||
 | 
			
		||||
Open MCT Web includes a Maven command line build. Although Open MCT Web
 | 
			
		||||
@@ -90,6 +103,21 @@ This build will:
 | 
			
		||||
 | 
			
		||||
Run as `mvn clean install`.
 | 
			
		||||
 | 
			
		||||
### Building Documentation
 | 
			
		||||
 | 
			
		||||
Open MCT Web's documentation is generated by an
 | 
			
		||||
[npm](https://www.npmjs.com/)-based build:
 | 
			
		||||
 | 
			
		||||
* `npm install` _(only needs to run once)_
 | 
			
		||||
* `npm run docs`
 | 
			
		||||
 | 
			
		||||
Documentation will be generated in `target/docs`. Note that diagram
 | 
			
		||||
generation is dependent on having [Cairo](http://cairographics.org/download/)
 | 
			
		||||
installed; see
 | 
			
		||||
[node-canvas](https://github.com/Automattic/node-canvas#installation)'s
 | 
			
		||||
documentation for help with installation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Glossary
 | 
			
		||||
 | 
			
		||||
Certain terms are used throughout Open MCT Web with consistent meanings
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
 | 
			
		||||
# Script to build and deploy docs to github pages.
 | 
			
		||||
 | 
			
		||||
OUTPUT_DIRECTORY="docs"
 | 
			
		||||
OUTPUT_DIRECTORY="target/docs"
 | 
			
		||||
REPOSITORY_URL="git@github.com:nasa/openmctweb.git"
 | 
			
		||||
 | 
			
		||||
BUILD_SHA=`git rev-parse head`
 | 
			
		||||
@@ -39,7 +39,7 @@ if [ -d $OUTPUT_DIRECTORY ]; then
 | 
			
		||||
    rm -rf $OUTPUT_DIRECTORY || exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
npm run-script jsdoc
 | 
			
		||||
npm run docs
 | 
			
		||||
cd $OUTPUT_DIRECTORY || exit 1
 | 
			
		||||
 | 
			
		||||
echo "git init"
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
    "platform/commonUI/dialog",
 | 
			
		||||
    "platform/commonUI/general",
 | 
			
		||||
    "platform/commonUI/inspect",
 | 
			
		||||
    "platform/commonUI/mobile",
 | 
			
		||||
    "platform/commonUI/themes/espresso",
 | 
			
		||||
    "platform/containment",
 | 
			
		||||
    "platform/execution",
 | 
			
		||||
    "platform/telemetry",
 | 
			
		||||
@@ -18,12 +20,14 @@
 | 
			
		||||
    "platform/features/scrolling",
 | 
			
		||||
    "platform/features/events",
 | 
			
		||||
    "platform/forms",
 | 
			
		||||
    "platform/identity",
 | 
			
		||||
    "platform/persistence/local",
 | 
			
		||||
    "platform/persistence/queue",
 | 
			
		||||
    "platform/policy",
 | 
			
		||||
    "platform/entanglement",
 | 
			
		||||
    "platform/search",
 | 
			
		||||
 | 
			
		||||
    "example/imagery",
 | 
			
		||||
    "example/persistence",
 | 
			
		||||
    "example/eventGenerator",
 | 
			
		||||
    "example/generator"
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,3 +4,11 @@ deployment:
 | 
			
		||||
    commands:
 | 
			
		||||
      - ./build-docs.sh
 | 
			
		||||
      - git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master
 | 
			
		||||
  openmctweb-staging-un:
 | 
			
		||||
    branch: search
 | 
			
		||||
    heroku:
 | 
			
		||||
      appname: openmctweb-staging-un
 | 
			
		||||
  openmctweb-staging-deux:
 | 
			
		||||
    branch: mobile
 | 
			
		||||
    heroku:
 | 
			
		||||
      appname: openmctweb-staging-deux
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										193
									
								
								docs/gendocs.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								docs/gendocs.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global require,process,GLOBAL*/
 | 
			
		||||
/*jslint nomen: false */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Usage:
 | 
			
		||||
//   node gendocs.js --in <source directory> --out <dest directory>
 | 
			
		||||
 | 
			
		||||
var CONSTANTS = {
 | 
			
		||||
        DIAGRAM_WIDTH: 800,
 | 
			
		||||
        DIAGRAM_HEIGHT: 500
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be defined
 | 
			
		||||
(function () {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    var fs = require("fs"),
 | 
			
		||||
        mkdirp = require("mkdirp"),
 | 
			
		||||
        path = require("path"),
 | 
			
		||||
        glob = require("glob"),
 | 
			
		||||
        marked = require("marked"),
 | 
			
		||||
        split = require("split"),
 | 
			
		||||
        stream = require("stream"),
 | 
			
		||||
        nomnoml = require('nomnoml'),
 | 
			
		||||
        Canvas = require('canvas'),
 | 
			
		||||
        options = require("minimist")(process.argv.slice(2));
 | 
			
		||||
 | 
			
		||||
    // Convert from nomnoml source to a target PNG file.
 | 
			
		||||
    function renderNomnoml(source, target) {
 | 
			
		||||
        var canvas =
 | 
			
		||||
            new Canvas(CONSTANTS.DIAGRAM_WIDTH, CONSTANTS.DIAGRAM_HEIGHT);
 | 
			
		||||
        nomnoml.draw(canvas, source, 1.0);
 | 
			
		||||
        canvas.pngStream().pipe(fs.createWriteStream(target));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Stream transform.
 | 
			
		||||
    // Pulls out nomnoml diagrams from fenced code blocks and renders them
 | 
			
		||||
    // as PNG files in the output directory, prefixed with a provided name.
 | 
			
		||||
    // The fenced code blocks will be replaced with Markdown in the
 | 
			
		||||
    // output of this stream.
 | 
			
		||||
    function nomnomlifier(outputDirectory, prefix) {
 | 
			
		||||
        var transform = new stream.Transform({ objectMode: true }),
 | 
			
		||||
            isBuilding = false,
 | 
			
		||||
            counter = 1,
 | 
			
		||||
            outputPath,
 | 
			
		||||
            source = "";
 | 
			
		||||
 | 
			
		||||
        transform._transform = function (chunk, encoding, done) {
 | 
			
		||||
            if (!isBuilding) {
 | 
			
		||||
                if (chunk.trim().indexOf("```nomnoml") === 0) {
 | 
			
		||||
                    var outputFilename = prefix + '-' + counter + '.png';
 | 
			
		||||
                    outputPath = path.join(outputDirectory, outputFilename);
 | 
			
		||||
                    this.push([
 | 
			
		||||
                        "\n\n\n"
 | 
			
		||||
                    ].join(""));
 | 
			
		||||
                    isBuilding = true;
 | 
			
		||||
                    source = "";
 | 
			
		||||
                    counter += 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Otherwise, pass through
 | 
			
		||||
                    this.push(chunk + '\n');
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (chunk.trim() === "```") {
 | 
			
		||||
                    // End nomnoml
 | 
			
		||||
                    renderNomnoml(source, outputPath);
 | 
			
		||||
                    isBuilding = false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    source += chunk + '\n';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            done();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return transform;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Convert from Github-flavored Markdown to HTML
 | 
			
		||||
    function gfmifier() {
 | 
			
		||||
        var transform = new stream.Transform({ objectMode: true }),
 | 
			
		||||
            markdown = "";
 | 
			
		||||
        transform._transform = function (chunk, encoding, done) {
 | 
			
		||||
            markdown += chunk;
 | 
			
		||||
            done();
 | 
			
		||||
        };
 | 
			
		||||
        transform._flush = function (done) {
 | 
			
		||||
            this.push("<html><body>\n");
 | 
			
		||||
            this.push(marked(markdown));
 | 
			
		||||
            this.push("\n</body></html>\n");
 | 
			
		||||
            done();
 | 
			
		||||
        };
 | 
			
		||||
        return transform;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Custom renderer for marked; converts relative links from md to html,
 | 
			
		||||
    // and makes headings linkable.
 | 
			
		||||
    function CustomRenderer() {
 | 
			
		||||
        var renderer = new marked.Renderer(),
 | 
			
		||||
            customRenderer = Object.create(renderer);
 | 
			
		||||
        customRenderer.heading = function (text, level) {
 | 
			
		||||
            var escapedText = (text || "").trim().toLowerCase().replace(/\W/g, "-"),
 | 
			
		||||
                aOpen = "<a name=\"" + escapedText + "\" href=\"#" + escapedText + "\">",
 | 
			
		||||
                aClose = "</a>";
 | 
			
		||||
            return aOpen + renderer.heading.apply(renderer, arguments) + aClose;
 | 
			
		||||
        };
 | 
			
		||||
        // Change links to .md files to .html
 | 
			
		||||
        customRenderer.link = function (href, title, text) {
 | 
			
		||||
            // ...but only if they look like relative paths
 | 
			
		||||
            return (href || "").indexOf(":") === -1 && href[0] !== "/" ?
 | 
			
		||||
                renderer.link(href.replace(/\.md/, ".html"), title, text) :
 | 
			
		||||
                renderer.link.apply(renderer, arguments);
 | 
			
		||||
        };
 | 
			
		||||
        return customRenderer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    options['in'] = options['in'] || options.i;
 | 
			
		||||
    options.out = options.out || options.o;
 | 
			
		||||
 | 
			
		||||
    marked.setOptions({
 | 
			
		||||
        renderer: new CustomRenderer(),
 | 
			
		||||
        gfm: true,
 | 
			
		||||
        tables: true,
 | 
			
		||||
        breaks: false,
 | 
			
		||||
        pedantic: false,
 | 
			
		||||
        sanitize: true,
 | 
			
		||||
        smartLists: true,
 | 
			
		||||
        smartypants: false
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Convert all markdown files.
 | 
			
		||||
    // First, pull out nomnoml diagrams.
 | 
			
		||||
    // Then, convert remaining Markdown to HTML.
 | 
			
		||||
    glob(options['in'] + "/**/*.md", {}, function (err, files) {
 | 
			
		||||
        files.forEach(function (file) {
 | 
			
		||||
            var destination = file.replace(options['in'], options.out)
 | 
			
		||||
                .replace(/md$/, "html"),
 | 
			
		||||
                destPath = path.dirname(destination),
 | 
			
		||||
                prefix = path.basename(destination).replace(/\.html$/, "");
 | 
			
		||||
 | 
			
		||||
            mkdirp(destPath, function (err) {
 | 
			
		||||
                fs.createReadStream(file, { encoding: 'utf8' })
 | 
			
		||||
                    .pipe(split())
 | 
			
		||||
                    .pipe(nomnomlifier(destPath, prefix))
 | 
			
		||||
                    .pipe(gfmifier())
 | 
			
		||||
                    .pipe(fs.createWriteStream(destination, {
 | 
			
		||||
                        encoding: 'utf8'
 | 
			
		||||
                    }));
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Also copy over all HTML, CSS, or PNG files
 | 
			
		||||
    glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) {
 | 
			
		||||
        files.forEach(function (file) {
 | 
			
		||||
            var destination = file.replace(options['in'], options.out),
 | 
			
		||||
                destPath = path.dirname(destination);
 | 
			
		||||
 | 
			
		||||
            mkdirp(destPath, function (err) {
 | 
			
		||||
                fs.createReadStream(file, { encoding: 'utf8' })
 | 
			
		||||
                    .pipe(fs.createWriteStream(destination, {
 | 
			
		||||
                        encoding: 'utf8'
 | 
			
		||||
                    }));
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
}());
 | 
			
		||||
							
								
								
									
										232
									
								
								docs/src/architecture/Framework.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								docs/src/architecture/Framework.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,232 @@
 | 
			
		||||
# Overview
 | 
			
		||||
 | 
			
		||||
The framework layer's most basic responsibility is allowing individual
 | 
			
		||||
software components to communicate. The software components it recognizes
 | 
			
		||||
are:
 | 
			
		||||
 | 
			
		||||
* _Extensions_: Individual units of functionality that can be added to
 | 
			
		||||
  or removed from Open MCT Web. _Extension categories_ distinguish what
 | 
			
		||||
  type of functionality is being added/removed.
 | 
			
		||||
* _Bundles_: A grouping of related extensions
 | 
			
		||||
  (named after an analogous concept from [OSGi](http://www.osgi.org/))
 | 
			
		||||
  that may be added or removed as a group.
 | 
			
		||||
 | 
			
		||||
The framework layer operates by taking a set of active bundles, and
 | 
			
		||||
exposing extensions to one another as-needed, using
 | 
			
		||||
[dependency injection](https://en.wikipedia.org/wiki/Dependency_injection).
 | 
			
		||||
Extensions are responsible for declaring their dependencies in a
 | 
			
		||||
manner which the framework layer can understand.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: down
 | 
			
		||||
[Open MCT Web|
 | 
			
		||||
  [Dependency injection framework]-->[Platform bundle #1]
 | 
			
		||||
  [Dependency injection framework]-->[Platform bundle #2]
 | 
			
		||||
  [Dependency injection framework]-->[Plugin bundle #1]
 | 
			
		||||
  [Dependency injection framework]-->[Plugin bundle #2]
 | 
			
		||||
  [Platform bundle #1|[Extensions]]
 | 
			
		||||
  [Platform bundle #2|[Extensions]]
 | 
			
		||||
  [Plugin bundle #1|[Extensions]]
 | 
			
		||||
  [Plugin bundle #2|[Extensions]]
 | 
			
		||||
  [Platform bundle #1]<->[Platform bundle #2]
 | 
			
		||||
  [Plugin bundle #1]<->[Platform bundle #2]
 | 
			
		||||
  [Plugin bundle #1]<->[Plugin bundle #2]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The "dependency injection framework" in this case is
 | 
			
		||||
[AngularJS](https://angularjs.org/). Open MCT Web's framework layer
 | 
			
		||||
is really just a thin wrapper over Angular that recognizes the
 | 
			
		||||
concepts of bundles and extensions (as declared in JSON files) and
 | 
			
		||||
registering extensions with Angular. It additionally acts as a
 | 
			
		||||
mediator between Angular and [RequireJS](http://requirejs.org/),
 | 
			
		||||
which is used to load JavaScript sources which implement
 | 
			
		||||
extensions.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[Framework layer|
 | 
			
		||||
  [AngularJS]<-[Framework Component]
 | 
			
		||||
  [RequireJS]<-[Framework Component]
 | 
			
		||||
  [Framework Component]1o-*[Bundles]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
It is worth noting that _no other components_ are "aware" of the
 | 
			
		||||
framework component directly; Angular and Require are _used by_ the
 | 
			
		||||
framework components, and extensions in various bundles will have
 | 
			
		||||
their dependencies satisfied by Angular as a consequence of registration
 | 
			
		||||
activities which were performed by the framework component.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Application Initialization
 | 
			
		||||
 | 
			
		||||
The framework component initializes an Open MCT Web application following
 | 
			
		||||
a simple sequence of steps.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[<state> Load bundles.json]
 | 
			
		||||
[Load bundles.json]->[<state> Load bundle.json files]
 | 
			
		||||
[Load bundle.json files]->[<state> Resolve implementations]
 | 
			
		||||
[Resolve implementations]->[<state> Register with Angular]
 | 
			
		||||
[Register with Angular]->[<state> Bootstrap application]
 | 
			
		||||
[Bootstrap application]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
1. __Loading bundles.json.__ A file named `bundles.json` is loaded to determine
 | 
			
		||||
   which bundles to load. Bundles are given in this file as relative paths
 | 
			
		||||
   which point to bundle directories.
 | 
			
		||||
2. __Load bundle.json files.__ Individual bundle definitions are loaded; a
 | 
			
		||||
   `bundle.json` file is expected in each bundle directory.
 | 
			
		||||
2. __Resolving implementations.__ Any scripts which provide implementations for
 | 
			
		||||
   extensions exposed by bundles are loaded, using RequireJS.
 | 
			
		||||
3. __Register with Angular.__ Resolved extensions are registered with Angular,
 | 
			
		||||
   such that they can be used by the application at run-time. This stage
 | 
			
		||||
   includes both registration of Angular built-ins (directives, controllers,
 | 
			
		||||
   routes, constants, and services) as well as registration of non-Angular
 | 
			
		||||
   extensions.
 | 
			
		||||
4. __Bootstrap application.__ Once all extensions have been registered,
 | 
			
		||||
   the Angular application
 | 
			
		||||
   [is bootstrapped](https://docs.angularjs.org/guide/bootstrap).
 | 
			
		||||
 | 
			
		||||
## Architectural Paradigm
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[Extension]
 | 
			
		||||
[Extension]o->[Dependency #1]
 | 
			
		||||
[Extension]o->[Dependency #2]
 | 
			
		||||
[Extension]o->[Dependency #3]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Open MCT Web's architecture relies on a simple premise: Individual units
 | 
			
		||||
(extensions) only have access to the dependencies they declare that they
 | 
			
		||||
need, and they acquire references to these dependencies via dependency
 | 
			
		||||
injection. This has several desirable traits:
 | 
			
		||||
 | 
			
		||||
* Programming to an interface is enforced. Any given dependency can be
 | 
			
		||||
  swapped out for something which exposes an equivalent interface. This
 | 
			
		||||
  improves flexibility against refactoring, simplifies testing, and
 | 
			
		||||
  provides a common mechanism for extension and reconfiguration.
 | 
			
		||||
* The dependencies of a unit must be explicitly defined. This means that
 | 
			
		||||
  it can be easily determined what a given unit's role is within the
 | 
			
		||||
  larger system, in terms of what other components it will interact with.
 | 
			
		||||
  It also helps to enforce good separation of concerns: When a set of
 | 
			
		||||
  declared dependencies becomes long it is obvious, and this is usually
 | 
			
		||||
  a sign that a given unit is involved in too many concerns and should
 | 
			
		||||
  be refactored into smaller pieces.
 | 
			
		||||
* Individual units do not need to be aware of the framework; they need
 | 
			
		||||
  only be aware of the interfaces to the components they specifically
 | 
			
		||||
  use. This avoids introducing a ubiquitous dependency upon the framework
 | 
			
		||||
  layer itself; it is plausible to modify or replace the framework
 | 
			
		||||
  without making changes to individual software components which run upon
 | 
			
		||||
  the framework.
 | 
			
		||||
 | 
			
		||||
A drawback to this approach is that it makes it difficult to define
 | 
			
		||||
"the architecture" of Open MCT Web, in terms of describing the specific
 | 
			
		||||
units that interact at run-time. The run-time architecture is determined
 | 
			
		||||
by the framework as the consequence of wiring together dependencies.
 | 
			
		||||
As such, the specific architecture of any given application built on
 | 
			
		||||
Open MCT Web can look very different.
 | 
			
		||||
 | 
			
		||||
Keeping that in mind, there are a few useful patterns supported by the
 | 
			
		||||
framework that are useful to keep in mind.
 | 
			
		||||
 | 
			
		||||
The specific service infrastructure provided by the platform is described
 | 
			
		||||
in the [Platform Architecture](Platform.md).
 | 
			
		||||
 | 
			
		||||
## Extension Categories
 | 
			
		||||
 | 
			
		||||
One of the capabilities that the framework component layers on top of
 | 
			
		||||
AngularJS is support for many-to-one dependencies. That is, a specific
 | 
			
		||||
extension may declare a dependency to _all extensions of a specific
 | 
			
		||||
category_, instead of being limited to declaring specific dependencies.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[Specific Extension] 1 o-> * [Extension of Some Category]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This is useful for introducing specific extension points to an application.
 | 
			
		||||
Some unit of software will depend upon all extensions of a given category
 | 
			
		||||
and integrate their behavior into the system in some fashion; plugin authors
 | 
			
		||||
can then add new extensions of that category to augment existing behaviors.
 | 
			
		||||
 | 
			
		||||
Some developers may be familiar with the use of registries to achieve
 | 
			
		||||
similar characteristics. This approach is similar, except that the registry
 | 
			
		||||
is effectively implicit whenever a new extension category is used or
 | 
			
		||||
depended-upon. This has some advantages over a more straightforward
 | 
			
		||||
registry-based approach:
 | 
			
		||||
 | 
			
		||||
* These many-to-one relationships are expressed as dependencies; an
 | 
			
		||||
  extension category is registered as having dependencies on all individual
 | 
			
		||||
  extensions of this category. This avoids ordering issues that may occur
 | 
			
		||||
  with more conventional registries, which may be observed before all
 | 
			
		||||
  dependencies are resolved.
 | 
			
		||||
* The need for service registries of specific types is removed, reducing
 | 
			
		||||
  the number of interfaces to manage within the system. Groups of
 | 
			
		||||
  extensions are provided as arrays.
 | 
			
		||||
 | 
			
		||||
## Composite Services
 | 
			
		||||
 | 
			
		||||
Composite services (registered via extension category `components`) are
 | 
			
		||||
a pattern supported by the framework. These allow service instances to
 | 
			
		||||
be built from multiple components at run-time; support for this pattern
 | 
			
		||||
allows additional bundles to introduce or modify behavior associated
 | 
			
		||||
with these services without modifying or replacing original service
 | 
			
		||||
instances.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: down
 | 
			
		||||
[<abstract> FooService]
 | 
			
		||||
[FooDecorator #1]--:>[FooService]
 | 
			
		||||
[FooDecorator #n]--:>[FooService]
 | 
			
		||||
[FooAggregator]--:>[FooService]
 | 
			
		||||
[FooProvider #1]--:>[FooService]
 | 
			
		||||
[FooProvider #n]--:>[FooService]
 | 
			
		||||
 | 
			
		||||
[FooDecorator #1]o->[<state> ...decorators...]
 | 
			
		||||
[...decorators...]o->[FooDecorator #n]
 | 
			
		||||
[FooDecorator #n]o->[FooAggregator]
 | 
			
		||||
[FooAggregator]o->[FooProvider #1]
 | 
			
		||||
[FooAggregator]o->[<state> ...providers...]
 | 
			
		||||
[FooAggregator]o->[FooProvider #n]
 | 
			
		||||
 | 
			
		||||
[FooDecorator #1]--[<note> Exposed as fooService]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this pattern, components all implement an interface which is
 | 
			
		||||
standardized for that service. Components additionally declare
 | 
			
		||||
that they belong to one of three types:
 | 
			
		||||
 | 
			
		||||
* __Providers.__ A provider actually implements the behavior
 | 
			
		||||
  (satisfies the contract) for that kind of service. For instance,
 | 
			
		||||
  if a service is responsible for looking up documents by an identifier,
 | 
			
		||||
  one provider may do so by querying a database, while another may
 | 
			
		||||
  do so by reading a static JSON document. From the outside, either
 | 
			
		||||
  provider would look the same (they expose the same interface) and
 | 
			
		||||
  they could be swapped out easily.
 | 
			
		||||
* __Aggregator.__ An aggregator takes many providers and makes them
 | 
			
		||||
  behave as one. Again, this implements the same interface as an
 | 
			
		||||
  individual provider, so users of the service do not need to be
 | 
			
		||||
  concerned about the difference between consulting many providers
 | 
			
		||||
  and consulting one. Continuing with the example of a service that
 | 
			
		||||
  looks up documents by identifiers, an aggregator here might consult
 | 
			
		||||
  all providers, and return any document is found (perhaps picking one
 | 
			
		||||
  over the other or merging documents if there are multiple matches.)
 | 
			
		||||
* __Decorators.__ A decorator exposes the same interface as other
 | 
			
		||||
  components, but instead of fully implementing the behavior associated
 | 
			
		||||
  with that kind of service, it only acts as an intermediary, delegating
 | 
			
		||||
  the actual behavior to a different component. Decorators may transform
 | 
			
		||||
  inputs or outputs, or initiate some side effects associated with a
 | 
			
		||||
  service. This is useful if certain common behavior associated with a
 | 
			
		||||
  service (caching, for instance) may be useful across many different
 | 
			
		||||
  implementations of that same service.
 | 
			
		||||
 | 
			
		||||
The framework will register extensions in this category such that an
 | 
			
		||||
aggregator will depend on all of its providers, and decorators will
 | 
			
		||||
depend upon on one another in a chain. The result of this compositing step
 | 
			
		||||
(the last decorator, if any; otherwise the aggregator, if any;
 | 
			
		||||
otherwise a single provider) will be exposed as a single service that
 | 
			
		||||
other extensions can acquire through dependency injection. Because all
 | 
			
		||||
components of the same type of service expose the same interface, users
 | 
			
		||||
of that service do not need to be aware that they are talking to an
 | 
			
		||||
aggregator or a provider, for instance.
 | 
			
		||||
							
								
								
									
										714
									
								
								docs/src/architecture/Platform.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										714
									
								
								docs/src/architecture/Platform.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,714 @@
 | 
			
		||||
# Overview
 | 
			
		||||
 | 
			
		||||
The Open MCT Web 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
 | 
			
		||||
  domain objects of various sorts.
 | 
			
		||||
* A variety of extension points for introducing new functionality
 | 
			
		||||
  of various kinds within the context of the common user interface.
 | 
			
		||||
* A service infrastructure to support building additional components.
 | 
			
		||||
 | 
			
		||||
## Platform Architecture
 | 
			
		||||
 | 
			
		||||
While the framework provides a more general architectural paradigm for
 | 
			
		||||
building application, the platform adds more specificity by defining
 | 
			
		||||
additional extension types and allowing for integration with back end
 | 
			
		||||
components.
 | 
			
		||||
 | 
			
		||||
The run-time architecture of an Open MCT Web application can be categorized
 | 
			
		||||
into certain high-level tiers:
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[DOM]->[<state> AngularJS]
 | 
			
		||||
[AngularJS]->[Presentation Layer]
 | 
			
		||||
[Presentation Layer]->[Information Model]
 | 
			
		||||
[Presentation Layer]->[Service Infrastructure]
 | 
			
		||||
[Information Model]->[Service Infrastructure]
 | 
			
		||||
[Service Infrastructure]->[<state> Browser APIs]
 | 
			
		||||
[Browser APIs]->[Back-end]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Applications built using Open MCT Web may add or configure functionality
 | 
			
		||||
in __any of these tiers__.
 | 
			
		||||
 | 
			
		||||
* _DOM_: The rendered HTML document, composed from HTML templates which
 | 
			
		||||
  have been processed by AngularJS and will be updated by AngularJS
 | 
			
		||||
  to reflect changes from the presentation layer. User interactions
 | 
			
		||||
  are initiated from here and invoke behavior in the presentation layer.
 | 
			
		||||
* [_Presentation layer_](#presentation-layer): The presentation layer
 | 
			
		||||
  is responsible for updating (and providing information to update)
 | 
			
		||||
  the displayed state of the application. The presentation layer consists
 | 
			
		||||
  primarily of _controllers_ and _directives_. The presentation layer is
 | 
			
		||||
  concerned with inspecting the information model and preparing it for
 | 
			
		||||
  display.
 | 
			
		||||
* [_Information model_](#information-model): The information model
 | 
			
		||||
  describes the state and behavior of the objects with which the user
 | 
			
		||||
  interacts.
 | 
			
		||||
* [_Service infrastructure_](#service-infrastructure): The service
 | 
			
		||||
  infrastructure is responsible for providing the underlying general
 | 
			
		||||
  functionality needed to support the information model. This includes
 | 
			
		||||
  exposing underlying sets of extensions and mediating with the
 | 
			
		||||
  back-end.
 | 
			
		||||
* _Back-end_: The back-end is out of the scope of Open MCT Web, except
 | 
			
		||||
  for the interfaces which are utilized by adapters participating in the
 | 
			
		||||
  service infrastructure.
 | 
			
		||||
 | 
			
		||||
## Application Start-up
 | 
			
		||||
 | 
			
		||||
Once the
 | 
			
		||||
[application has been initialized](Framework.md#application-initialization)
 | 
			
		||||
Open MCT Web primarily operates in an event-driven paradigm; various
 | 
			
		||||
events (mouse clicks, timers firing, receiving responses to XHRs) trigger
 | 
			
		||||
the invocation of functions, typically in the presentation layer for
 | 
			
		||||
user actions or in the service infrastructure for server responses.
 | 
			
		||||
 | 
			
		||||
The "main point of entry" into an initialized Open MCT Web application
 | 
			
		||||
is effectively the
 | 
			
		||||
[route](https://docs.angularjs.org/api/ngRoute/service/$route#example)
 | 
			
		||||
which is associated with the URL used to access Open MCT Web (or a
 | 
			
		||||
default route.) This route will be associated with a template which
 | 
			
		||||
will be displayed; this template will include references to directives
 | 
			
		||||
and controllers which will be interpreted by Angular and used to
 | 
			
		||||
initialize the state of the display in a manner which is backed by
 | 
			
		||||
both the information model and the service infrastructure.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[<state> page load]
 | 
			
		||||
[page load]->[<state> route selection]
 | 
			
		||||
[route selection]->[<state> compile, display template]
 | 
			
		||||
[compile, display template]->[Template]
 | 
			
		||||
[Template]->[<state> use Controllers]
 | 
			
		||||
[Template]->[<state> use Directives]
 | 
			
		||||
[use Controllers]->[Controllers]
 | 
			
		||||
[use Directives]->[Directives]
 | 
			
		||||
[Controllers]->[<state> consult information model]
 | 
			
		||||
[consult information model]->[<state> expose data]
 | 
			
		||||
[expose data]->[Angular]
 | 
			
		||||
[Angular]->[<state> update display]
 | 
			
		||||
[Directives]->[<state> add event listeners]
 | 
			
		||||
[Directives]->[<state> update display]
 | 
			
		||||
[add event listeners]->[<end> End]
 | 
			
		||||
[update display]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Presentation Layer
 | 
			
		||||
 | 
			
		||||
The presentation layer of Open MCT Web is responsible for providing
 | 
			
		||||
information to display within templates, and for handling interactions
 | 
			
		||||
which are initiated from templated DOM elements. AngularJS acts as
 | 
			
		||||
an intermediary between the web page as the user sees it, and the
 | 
			
		||||
presentation layer implemented as Open MCT Web extensions.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[Presentation Layer|
 | 
			
		||||
  [Angular built-ins|
 | 
			
		||||
    [routes]
 | 
			
		||||
    [controllers]
 | 
			
		||||
    [directives]
 | 
			
		||||
    [templates]
 | 
			
		||||
  ]
 | 
			
		||||
  [Domain object representation|
 | 
			
		||||
    [views]
 | 
			
		||||
    [representations]
 | 
			
		||||
    [representers]
 | 
			
		||||
    [gestures]
 | 
			
		||||
  ]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Angular built-ins
 | 
			
		||||
 | 
			
		||||
Several extension categories in the presentation layer map directly
 | 
			
		||||
to primitives from AngularJS:
 | 
			
		||||
 | 
			
		||||
* [_Controllers_](https://docs.angularjs.org/guide/controller) provide
 | 
			
		||||
  data to templates, and expose functionality that can be called from
 | 
			
		||||
  templates.
 | 
			
		||||
* [_Directives_](https://docs.angularjs.org/guide/directive) effectively
 | 
			
		||||
  extend HTML to provide custom behavior associated with specific
 | 
			
		||||
  attributes and tags.
 | 
			
		||||
* [_Routes_](https://docs.angularjs.org/api/ngRoute/service/$route#example)
 | 
			
		||||
  are used to associate specific URLs (including the fragment identifier)
 | 
			
		||||
  with specific application states. (In Open MCT Web, these are used to
 | 
			
		||||
  describe the mode of usage - e.g. browse or edit - as well as to
 | 
			
		||||
  identify the object being used.)
 | 
			
		||||
* [_Templates_](https://docs.angularjs.org/guide/templates) are partial
 | 
			
		||||
  HTML documents that will be rendered and kept up-to-date by AngularJS.
 | 
			
		||||
  Open MCT Web introduces a custom `mct-include` directive which acts
 | 
			
		||||
  as a wrapper around `ng-include` to allow templates to be referred
 | 
			
		||||
  to by symbolic names.
 | 
			
		||||
 | 
			
		||||
## Domain object representation
 | 
			
		||||
 | 
			
		||||
The remaining extension categories in the presentation layer are specific
 | 
			
		||||
to displaying domain objects.
 | 
			
		||||
 | 
			
		||||
* _Representations_ are templates that will be used to display
 | 
			
		||||
  domain objects in specific ways (e.g. "as a tree node.")
 | 
			
		||||
* _Views_ are representations which are exposed to the user as options
 | 
			
		||||
  for displaying domain objects.
 | 
			
		||||
* _Representers_ are extensions which modify or augment the process
 | 
			
		||||
  of representing domain objects generally (e.g. by attaching
 | 
			
		||||
  gestures to them.)
 | 
			
		||||
* _Gestures_ provide associations between specific user actions
 | 
			
		||||
  (expressed as DOM events) and resulting behavior upon domain objects
 | 
			
		||||
  (typically expressed as members of the `actions` extension category)
 | 
			
		||||
  that can be reused across domain objects. For instance, `drag` and
 | 
			
		||||
  `drop` are both gestures associated with using drag-and-drop to
 | 
			
		||||
  modify the composition of domain objects by interacting with their
 | 
			
		||||
  representations.
 | 
			
		||||
 | 
			
		||||
# Information Model
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[Information Model|
 | 
			
		||||
  [DomainObject|
 | 
			
		||||
    getId() : string
 | 
			
		||||
    getModel() : object
 | 
			
		||||
    getCapability(key : string) : Capability
 | 
			
		||||
    hasCapability(key : string) : boolean
 | 
			
		||||
    useCapability(key : string, args...) : *
 | 
			
		||||
  ]
 | 
			
		||||
  [DomainObject] 1 +- 1 [Model]
 | 
			
		||||
  [DomainObject] 1 o- * [Capability]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Domain objects are the most fundamental component of Open MCT Web's
 | 
			
		||||
information model. A domain object is some distinct thing relevant to a
 | 
			
		||||
user's work flow, such as a telemetry channel, display, or similar.
 | 
			
		||||
Open MCT Web is a tool for viewing, browsing, manipulating, and otherwise
 | 
			
		||||
interacting with a graph of domain objects.
 | 
			
		||||
 | 
			
		||||
A domain object should be conceived of as the union of the following:
 | 
			
		||||
 | 
			
		||||
* _Identifier_: A machine-readable string that uniquely identifies the
 | 
			
		||||
  domain object within this application instance.
 | 
			
		||||
* _Model_: The persistent state of the domain object. A domain object's
 | 
			
		||||
  model is a JavaScript object that can be losslessly converted to JSON.
 | 
			
		||||
* _Capabilities_: Dynamic behavior associated with the domain object.
 | 
			
		||||
  Capabilities are JavaScript objects which provide additional methods
 | 
			
		||||
  for interacting with the domain objects which expose those capabilities.
 | 
			
		||||
  Not all domain objects expose all capabilities. The interface exposed
 | 
			
		||||
  by any given capability will depend on its type (as identified
 | 
			
		||||
  by the `key` argument.) For instance, a `persistence` capability
 | 
			
		||||
  has a different interface from a `telemetry` capability. Using
 | 
			
		||||
  capabilities requires some prior knowledge of their interface.
 | 
			
		||||
 | 
			
		||||
## Capabilities and Services
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[DomainObject]o-[FooCapability]
 | 
			
		||||
[FooCapability]o-[FooService]
 | 
			
		||||
[FooService]o-[foos]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
At run-time, the user is primarily concerned with interacting with
 | 
			
		||||
domain objects. These interactions are ultimately supported via back-end
 | 
			
		||||
services, but to allow customization per-object, these are often mediated
 | 
			
		||||
by capabilities.
 | 
			
		||||
 | 
			
		||||
A common pattern that emerges in the Open MCT Platform is as follows:
 | 
			
		||||
 | 
			
		||||
* A `DomainObject` has some particular behavior that will be supported
 | 
			
		||||
  by a service.
 | 
			
		||||
* A `Capability` of that domain object will define that behavior,
 | 
			
		||||
  _for that domain object_, supported by a service.
 | 
			
		||||
* A `Service` utilized by that capability will perform the actual behavior.
 | 
			
		||||
* An extension category will be utilized by that capability to determine
 | 
			
		||||
  the set of possible behaviors.
 | 
			
		||||
 | 
			
		||||
Concrete examples of capabilities which follow this pattern
 | 
			
		||||
(or a subset of this pattern) include:
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[DomainObject]1 o- *[Capability]
 | 
			
		||||
[Capability]<:--[TypeCapability]
 | 
			
		||||
[Capability]<:--[ActionCapability]
 | 
			
		||||
[Capability]<:--[PersistenceCapability]
 | 
			
		||||
[Capability]<:--[TelemetryCapability]
 | 
			
		||||
[TypeCapability]o-[TypeService]
 | 
			
		||||
[TypeService]o-[types]
 | 
			
		||||
[ActionCapability]o-[ActionService]
 | 
			
		||||
[ActionService]o-[actions]
 | 
			
		||||
[PersistenceCapability]o-[PersistenceService]
 | 
			
		||||
[TelemetryCapability]o-[TelemetryService]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Service Infrastructure
 | 
			
		||||
 | 
			
		||||
Most services exposed by the Open MCT Web platform follow the
 | 
			
		||||
[composite services](Framework.md#composite-services) to permit
 | 
			
		||||
a higher degree of flexibility in how a service can be modified
 | 
			
		||||
or customized for specific applications.
 | 
			
		||||
 | 
			
		||||
To simplify usage for plugin developers, the platform also usually
 | 
			
		||||
includes a provider implementation for these service type that consumes
 | 
			
		||||
some extension category. For instance, an `ActionService` provider is
 | 
			
		||||
included which depends upon extension category `actions`, and exposes
 | 
			
		||||
all actions declared as such to the system. As such, plugin developers
 | 
			
		||||
can simply implement the new actions they wish to be made available without
 | 
			
		||||
worrying about the details of composite services or implementing a new
 | 
			
		||||
`ActionService` provider; however, the ability to implement a new provider
 | 
			
		||||
remains useful when the expressive power of individual extensions is
 | 
			
		||||
insufficient.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[ Service Infrastructure |
 | 
			
		||||
  [ObjectService]->[ModelService]
 | 
			
		||||
  [ModelService]->[PersistenceService]
 | 
			
		||||
  [ObjectService]->[CapabilityService]
 | 
			
		||||
  [CapabilityService]->[capabilities]
 | 
			
		||||
  [capabilities]->[TelemetryService]
 | 
			
		||||
  [capabilities]->[PersistenceService]
 | 
			
		||||
  [capabilities]->[TypeService]
 | 
			
		||||
  [capabilities]->[ActionService]
 | 
			
		||||
  [capabilities]->[ViewService]
 | 
			
		||||
  [PersistenceService]->[<database> Document store]
 | 
			
		||||
  [TelemetryService]->[<database> Telemetry source]
 | 
			
		||||
  [ActionService]->[actions]
 | 
			
		||||
  [ActionService]->[PolicyService]
 | 
			
		||||
  [ViewService]->[PolicyService]
 | 
			
		||||
  [ViewService]->[views]
 | 
			
		||||
  [PolicyService]->[policies]
 | 
			
		||||
  [TypeService]->[types]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A short summary of the roles of these services:
 | 
			
		||||
 | 
			
		||||
* _[ObjectService](#object-service)_: Allows retrieval of domain objects by
 | 
			
		||||
  their identifiers; in practice, often the main point of entry into the
 | 
			
		||||
  [information model](#information-model).
 | 
			
		||||
* _[ModelService](#model-service)_: Provides domain object models, retrieved
 | 
			
		||||
  by their identifier.
 | 
			
		||||
* _[CapabilityService](#capability-service)_: Provides capabilities, as they
 | 
			
		||||
  apply to specific domain objects (as judged from their model.)
 | 
			
		||||
* _[TelemetryService](#telemetry-service)_: Provides access to historical
 | 
			
		||||
  and real-time telemetry data.
 | 
			
		||||
* _[PersistenceService](#persistence-service)_: Provides the ability to
 | 
			
		||||
  store and retrieve documents (such as domain object models.)
 | 
			
		||||
* _[ActionService](#action-service)_: Provides distinct user actions that
 | 
			
		||||
  can take place within the system (typically, upon or using domain objects.)
 | 
			
		||||
* _[ViewService](#view-service)_: Provides views for domain objects. A view
 | 
			
		||||
  is a user-selectable representation of a domain object (in practice, an
 | 
			
		||||
  HTML template.)
 | 
			
		||||
* _[PolicyService](#policy-service)_: Handles decisions about which
 | 
			
		||||
  behavior are allowed within certain specific contexts.
 | 
			
		||||
* _[TypeService](#type-service)_: Provides information to distinguish
 | 
			
		||||
  different types of domain objects from one another within the system.
 | 
			
		||||
 | 
			
		||||
## Object Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[<abstract> ObjectService|
 | 
			
		||||
  getObjects(ids : Array.<string>) : Promise.<object.<string, DomainObject>>
 | 
			
		||||
]
 | 
			
		||||
[DomainObjectProvider]--:>[ObjectService]
 | 
			
		||||
[DomainObjectProvider]o-[ModelService]
 | 
			
		||||
[DomainObjectProvider]o-[CapabilityService]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
As domain objects are central to Open MCT Web's information model,
 | 
			
		||||
acquiring domain objects is equally important.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[<start> Start]->[<state> Look up models]
 | 
			
		||||
[<state> Look up models]->[<state> Look up capabilities]
 | 
			
		||||
[<state> Look up capabilities]->[<state> Instantiate DomainObject]
 | 
			
		||||
[<state> Instantiate DomainObject]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Open MCT Web includes an implementation of an `ObjectService` which
 | 
			
		||||
satisfies this capability by:
 | 
			
		||||
 | 
			
		||||
* Consulting the [Model Service](#model-service) to acquire domain object
 | 
			
		||||
  models by identifier.
 | 
			
		||||
* Passing these models to a [Capability Service](#capability-service) to
 | 
			
		||||
  determine which capabilities are applicable.
 | 
			
		||||
* Combining these results together as [DomainObject](#information-model)
 | 
			
		||||
  instances.
 | 
			
		||||
 | 
			
		||||
## Model Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: down
 | 
			
		||||
[<abstract> ModelService|
 | 
			
		||||
  getModels(ids : Array.<string>) : Promise.<object.<string, object>>
 | 
			
		||||
]
 | 
			
		||||
[StaticModelProvider]--:>[ModelService]
 | 
			
		||||
[RootModelProvider]--:>[ModelService]
 | 
			
		||||
[PersistedModelProvider]--:>[ModelService]
 | 
			
		||||
[ModelAggregator]--:>[ModelService]
 | 
			
		||||
[CachingModelDecorator]--:>[ModelService]
 | 
			
		||||
[MissingModelDecorator]--:>[ModelService]
 | 
			
		||||
 | 
			
		||||
[MissingModelDecorator]o-[CachingModelDecorator]
 | 
			
		||||
[CachingModelDecorator]o-[ModelAggregator]
 | 
			
		||||
[ModelAggregator]o-[StaticModelProvider]
 | 
			
		||||
[ModelAggregator]o-[RootModelProvider]
 | 
			
		||||
[ModelAggregator]o-[PersistedModelProvider]
 | 
			
		||||
 | 
			
		||||
[PersistedModelProvider]o-[PersistenceService]
 | 
			
		||||
[RootModelProvider]o-[roots]
 | 
			
		||||
[StaticModelProvider]o-[models]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The platform's model service is responsible for providing domain object
 | 
			
		||||
models (effectively, JSON documents describing the persistent state
 | 
			
		||||
associated with domain objects.) These are retrieved by identifier.
 | 
			
		||||
 | 
			
		||||
The platform includes multiple components of this variety:
 | 
			
		||||
 | 
			
		||||
* `PersistedModelProvider` looks up domain object models from
 | 
			
		||||
  a persistence store (the [`PersistenceService`](#persistence-service));
 | 
			
		||||
  this is how user-created and user-modified
 | 
			
		||||
  domain object models are retrieved.
 | 
			
		||||
* `RootModelProvider` provides domain object models that have been
 | 
			
		||||
  declared via the `roots` extension category. These will appear at the
 | 
			
		||||
  top level of the tree hierarchy in the user interface.
 | 
			
		||||
* `StaticModelProvider` provides domain object models that have been
 | 
			
		||||
  declared via the `models` extension category. This is useful for
 | 
			
		||||
  allowing plugins to expose new domain objects declaratively.
 | 
			
		||||
* `ModelAggregator` merges together the results from multiple providers.
 | 
			
		||||
  If multiple providers return models for the same domain object,
 | 
			
		||||
  the most recently modified version (as determined by the `modified`
 | 
			
		||||
  property of the model) is chosen.
 | 
			
		||||
* `CachingModelDecorator` caches model instances in memory. This
 | 
			
		||||
  ensures that only a single instance of a domain object model is
 | 
			
		||||
  present at any given time within the application, and prevent
 | 
			
		||||
  redundant retrievals.
 | 
			
		||||
* `MissingModelDecorator` adds in placeholders when no providers
 | 
			
		||||
  have returned domain object models for a specific identifier. This
 | 
			
		||||
  allows the user to easily see that something was expected to be
 | 
			
		||||
  present, but wasn't.
 | 
			
		||||
 | 
			
		||||
## Capability Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: down
 | 
			
		||||
[<abstract> CapabilityService|
 | 
			
		||||
  getCapabilities(model : object) : object.<string, Function>
 | 
			
		||||
]
 | 
			
		||||
[CoreCapabilityProvider]--:>[CapabilityService]
 | 
			
		||||
[QueuingPersistenceCapabilityDecorator]--:>[CapabilityService]
 | 
			
		||||
 | 
			
		||||
[CoreCapabilityProvider]o-[capabilities]
 | 
			
		||||
[QueuingPersistenceCapabilityDecorator]o-[CoreCapabilityProvider]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The capability service is responsible for determining which capabilities
 | 
			
		||||
are applicable for a given domain object, based on its model. Primarily,
 | 
			
		||||
this is handled by the `CoreCapabilityProvider`, which examines
 | 
			
		||||
capabilities exposed via the `capabilities` extension category.
 | 
			
		||||
 | 
			
		||||
Additionally, `platform/persistence/queue` decorates the persistence
 | 
			
		||||
capability specifically to batch persistence attempts among multiple
 | 
			
		||||
objects (this allows failures to be recognized and handled in groups.)
 | 
			
		||||
 | 
			
		||||
## Telemetry Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<abstract> TelemetryService|
 | 
			
		||||
  requestData(requests : Array.<TelemetryRequest>) : Promise.<object>
 | 
			
		||||
  subscribe(requests : Array.<TelemetryRequest>) : Function
 | 
			
		||||
]<--:[TelemetryAggregator]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The telemetry service is responsible for acquiring telemetry data.
 | 
			
		||||
 | 
			
		||||
Notably, the platform does not include any providers for
 | 
			
		||||
`TelemetryService`; applications built on Open MCT Web will need to
 | 
			
		||||
implement a provider for this service if they wish to expose telemetry
 | 
			
		||||
data. This is usually the most important step for integrating Open MCT Web
 | 
			
		||||
into an existing telemetry system.
 | 
			
		||||
 | 
			
		||||
Requests for telemetry data are usually initiated in the
 | 
			
		||||
[presentation layer](#presentation-layer) by some `Controller` referenced
 | 
			
		||||
from a view. The `telemetryHandler` service is most commonly used (although
 | 
			
		||||
one could also use an object's `telemetry` capability directly) as this
 | 
			
		||||
handles capability delegation, by which a domain object such as a Telemetry
 | 
			
		||||
Panel can declare that its `telemetry` capability should be handled by the
 | 
			
		||||
objects it contains. Ultimately, the request for historical data and the
 | 
			
		||||
new subscriptions will reach the `TelemetryService`, and, by way of the
 | 
			
		||||
provider(s) which are present for that `TelemetryService`, will pass the
 | 
			
		||||
same requests to the back-end.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[Controller]
 | 
			
		||||
[Controller]->[<state> declares object of interest]
 | 
			
		||||
[declares object of interest]->[TelemetryHandler]
 | 
			
		||||
[TelemetryHandler]->[<state> requests telemetry from capabilities]
 | 
			
		||||
[TelemetryHandler]->[<state> subscribes to telemetry using capabilities]
 | 
			
		||||
[requests telemetry from capabilities]->[TelemetryCapability]
 | 
			
		||||
[subscribes to telemetry using capabilities]->[TelemetryCapability]
 | 
			
		||||
[TelemetryCapability]->[<state> requests telemetry]
 | 
			
		||||
[TelemetryCapability]->[<state> subscribes to telemetry]
 | 
			
		||||
[requests telemetry]->[TelemetryService]
 | 
			
		||||
[subscribes to telemetry]->[TelemetryService]
 | 
			
		||||
[TelemetryService]->[<state> issues request]
 | 
			
		||||
[TelemetryService]->[<state> updates subscriptions]
 | 
			
		||||
[TelemetryService]->[<state> listens for real-time data]
 | 
			
		||||
[issues request]->[<database> Telemetry Back-end]
 | 
			
		||||
[updates subscriptions]->[Telemetry Back-end]
 | 
			
		||||
[listens for real-time data]->[Telemetry Back-end]
 | 
			
		||||
[Telemetry Back-end]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The back-end, in turn, is expected to provide whatever historical
 | 
			
		||||
telemetry is available to satisfy the request that has been issue.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[<database> Telemetry Back-end]
 | 
			
		||||
[Telemetry Back-end]->[<state> transmits historical telemetry]
 | 
			
		||||
[transmits historical telemetry]->[TelemetryService]
 | 
			
		||||
[TelemetryService]->[<state> packages telemetry, fulfills requests]
 | 
			
		||||
[packages telemetry, fulfills requests]->[TelemetryCapability]
 | 
			
		||||
[TelemetryCapability]->[<state> unpacks telemetry per-object, fulfills request]
 | 
			
		||||
[unpacks telemetry per-object, fulfills request]->[TelemetryHandler]
 | 
			
		||||
[TelemetryHandler]->[<state> exposes data]
 | 
			
		||||
[TelemetryHandler]->[<state> notifies controller]
 | 
			
		||||
[exposes data]->[Controller]
 | 
			
		||||
[notifies controller]->[Controller]
 | 
			
		||||
[Controller]->[<state> prepares data for template]
 | 
			
		||||
[prepares data for template]->[Template]
 | 
			
		||||
[Template]->[<state> displays data]
 | 
			
		||||
[displays data]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
One peculiarity of this approach is that we package many responses
 | 
			
		||||
together at once in the `TelemetryService`, then unpack these in the
 | 
			
		||||
`TelemetryCapability`, then repackage these in the `TelemetryHandler`.
 | 
			
		||||
The rationale for this is as follows:
 | 
			
		||||
 | 
			
		||||
* In the `TelemetryService`, we want to have the ability to combine
 | 
			
		||||
  multiple requests into one call to the back-end, as many back-ends
 | 
			
		||||
  will support this. It follows that we give the response as a single
 | 
			
		||||
  object, packages in a manner that allows responses to individual
 | 
			
		||||
  requests to be easily identified.
 | 
			
		||||
* In the `TelemetryCapability`, we want to provide telemetry for a
 | 
			
		||||
  _single object_, so the telemetry data gets unpacked. This allows
 | 
			
		||||
  for the unpacking of data to be handled in a single place, and
 | 
			
		||||
  also permits a flexible substitution method; domain objects may have
 | 
			
		||||
  implementations of the `telemetry` capability that do not use the
 | 
			
		||||
  `TelemetryService` at all, while still maintaining compatibility
 | 
			
		||||
  with any presentation layer code written to utilize this capability.
 | 
			
		||||
  (This is true of capabilities generally.)
 | 
			
		||||
* In the `TelemetryHandler`, we want to group multiple responses back
 | 
			
		||||
  together again to make it easy for the presentation layer to consume.
 | 
			
		||||
  In this case, the grouping is different from what may have occurred
 | 
			
		||||
  in the `TelemetryService`; this grouping is based on what is expected
 | 
			
		||||
  to be useful _in a specific view_. The `TelemetryService`
 | 
			
		||||
  may be receiving requests from multiple views.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[<database> Telemetry Back-end]
 | 
			
		||||
[Telemetry Back-end]->[<state> notifies client of new data]
 | 
			
		||||
[notifies client of new data]->[TelemetryService]
 | 
			
		||||
[TelemetryService]->[<choice> relevant subscribers?]
 | 
			
		||||
[relevant subscribers?] yes ->[<state> notify subscribers]
 | 
			
		||||
[relevant subscribers?] no ->[<state> ignore]
 | 
			
		||||
[ignore]->[<end> Ignored]
 | 
			
		||||
[notify subscribers]->[TelemetryCapability]
 | 
			
		||||
[TelemetryCapability]->[<state> notify listener]
 | 
			
		||||
[notify listener]->[TelemetryHandler]
 | 
			
		||||
[TelemetryHandler]->[<state> exposes data]
 | 
			
		||||
[TelemetryHandler]->[<state> notifies controller]
 | 
			
		||||
[exposes data]->[Controller]
 | 
			
		||||
[notifies controller]->[Controller]
 | 
			
		||||
[Controller]->[<state> prepares data for template]
 | 
			
		||||
[prepares data for template]->[Template]
 | 
			
		||||
[Template]->[<state> displays data]
 | 
			
		||||
[displays data]->[<end> End]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The flow of real-time data is similar, and is handled by a sequence
 | 
			
		||||
of callbacks between the presentation layer component which is
 | 
			
		||||
interested in data and the telemetry service. Providers in the
 | 
			
		||||
telemetry service listen to the back-end for new data (via whatever
 | 
			
		||||
mechanism their specific back-end supports), package this data in
 | 
			
		||||
the same manner as historical data, and pass that to the callbacks
 | 
			
		||||
which are associated with relevant requests.
 | 
			
		||||
 | 
			
		||||
## Persistence Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[<abstract> PersistenceService|
 | 
			
		||||
  listSpaces() : Promise.<Array.<string>>
 | 
			
		||||
  listObjects() : Promise.<Array.<string>>
 | 
			
		||||
  createObject(space : string, key : string, document : object) : Promise.<boolean>
 | 
			
		||||
  readObject(space : string, key : string, document : object) : Promise.<object>
 | 
			
		||||
  updateObject(space : string, key : string, document : object) : Promise.<boolean>
 | 
			
		||||
  deleteObject(space : string, key : string, document : object) : Promise.<boolean>
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[ElasticPersistenceProvider]--:>[PersistenceService]
 | 
			
		||||
[ElasticPersistenceProvider]->[<database> ElasticSearch]
 | 
			
		||||
 | 
			
		||||
[CouchPersistenceProvider]--:>[PersistenceService]
 | 
			
		||||
[CouchPersistenceProvider]->[<database> CouchDB]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Closely related to the notion of domain objects models is their
 | 
			
		||||
persistence. The `PersistenceService` allows these to be saved
 | 
			
		||||
and loaded. (Currently, this capability is only used for domain
 | 
			
		||||
object models, but the interface has been designed without this idea
 | 
			
		||||
in mind; other kinds of documents could be saved and loaded in the
 | 
			
		||||
same manner.)
 | 
			
		||||
 | 
			
		||||
There is no single definitive implementation of a `PersistenceService` in
 | 
			
		||||
the platform. Optional adapters are provided to store and load documents
 | 
			
		||||
from CouchDB and ElasticSearch, respectively; plugin authors may also
 | 
			
		||||
write additional adapters to utilize different back end technologies.
 | 
			
		||||
 | 
			
		||||
## Action Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[ActionService|
 | 
			
		||||
  getActions(context : ActionContext) : Array.<Action>
 | 
			
		||||
]
 | 
			
		||||
[ActionProvider]--:>[ActionService]
 | 
			
		||||
[CreateActionProvider]--:>[ActionService]
 | 
			
		||||
[ActionAggregator]--:>[ActionService]
 | 
			
		||||
[LoggingActionDecorator]--:>[ActionService]
 | 
			
		||||
[PolicyActionDecorator]--:>[ActionService]
 | 
			
		||||
 | 
			
		||||
[LoggingActionDecorator]o-[PolicyActionDecorator]
 | 
			
		||||
[PolicyActionDecorator]o-[ActionAggregator]
 | 
			
		||||
[ActionAggregator]o-[ActionProvider]
 | 
			
		||||
[ActionAggregator]o-[CreateActionProvider]
 | 
			
		||||
 | 
			
		||||
[ActionProvider]o-[actions]
 | 
			
		||||
[CreateActionProvider]o-[TypeService]
 | 
			
		||||
[PolicyActionDecorator]o-[PolicyService]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Actions are discrete tasks or behaviors that can be initiated by a user
 | 
			
		||||
upon or using a domain object. Actions may appear as menu items or
 | 
			
		||||
buttons in the user interface, or may be triggered by certain gestures.
 | 
			
		||||
 | 
			
		||||
Responsibilities of platform components of the action service are as
 | 
			
		||||
follows:
 | 
			
		||||
 | 
			
		||||
* `ActionProvider` exposes actions registered via extension category
 | 
			
		||||
  `actions`, supporting simple addition of new actions. Actions are
 | 
			
		||||
  filtered down to match action contexts based on criteria defined as
 | 
			
		||||
  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
 | 
			
		||||
  are generated after looking up which actions are available from the
 | 
			
		||||
  [`TypeService`](#type-service).
 | 
			
		||||
* `ActionAggregator` merges together actions from multiple providers.
 | 
			
		||||
* `PolicyActionDecorator` enforces the `action` policy category by
 | 
			
		||||
  filtering out actions which violate this policy, as determined by
 | 
			
		||||
  consulting the [`PolicyService`](#policy-service).
 | 
			
		||||
* `LoggingActionDecorator` wraps exposed actions and writes to the
 | 
			
		||||
  console when they are performed.
 | 
			
		||||
 | 
			
		||||
## View Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[ViewService|
 | 
			
		||||
  getViews(domainObject : DomainObject) : Array.<View>
 | 
			
		||||
]
 | 
			
		||||
[ViewProvider]--:>[ViewService]
 | 
			
		||||
[PolicyViewDecorator]--:>[ViewService]
 | 
			
		||||
 | 
			
		||||
[ViewProvider]o-[views]
 | 
			
		||||
[PolicyViewDecorator]o-[ViewProvider]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The view service provides views that are relevant to a specified domain
 | 
			
		||||
object. A "view" is a user-selectable visualization of a domain object.
 | 
			
		||||
 | 
			
		||||
The responsibilities of components of the view service are as follows:
 | 
			
		||||
 | 
			
		||||
* `ViewProvider` exposes views registered via extension category
 | 
			
		||||
  `views`, supporting simple addition of new views. Views are
 | 
			
		||||
  filtered down to match domain objects based on criteria defined as
 | 
			
		||||
  part of a view's extension definition.
 | 
			
		||||
* `PolicyViewDecorator` enforces the `view` policy category by
 | 
			
		||||
  filtering out views which violate this policy, as determined by
 | 
			
		||||
  consulting the [`PolicyService`](#policy-service).
 | 
			
		||||
 | 
			
		||||
## Policy Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[PolicyService|
 | 
			
		||||
  allow(category : string, candidate : object, context : object, callback? : Function) : boolean
 | 
			
		||||
]
 | 
			
		||||
[PolicyProvider]--:>[PolicyService]
 | 
			
		||||
[PolicyProvider]o-[policies]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The policy service provides a general-purpose extensible decision-making
 | 
			
		||||
mechanism; plugins can add new extensions of category `policies` to
 | 
			
		||||
modify decisions of a known category.
 | 
			
		||||
 | 
			
		||||
Often, the policy service is referenced from a decorator for another
 | 
			
		||||
service, to filter down the results of using that service based on some
 | 
			
		||||
appropriate policy category.
 | 
			
		||||
 | 
			
		||||
The policy provider works by looking up all registered policy extensions
 | 
			
		||||
which are relevant to a particular _category_, then consulting each in
 | 
			
		||||
order to see if they allow a particular _candidate_ in a particular
 | 
			
		||||
_context_; the types for the `candidate` and `context` arguments will
 | 
			
		||||
vary depending on the `category`. Any one policy may disallow the
 | 
			
		||||
decision as a whole.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[<start> Start]->[<state> is something allowed?]
 | 
			
		||||
[is something allowed?]->[PolicyService]
 | 
			
		||||
[PolicyService]->[<state> look up relevant policies by category]
 | 
			
		||||
[look up relevant policies by category]->[<state> consult policy #1]
 | 
			
		||||
[consult policy #1]->[Policy #1]
 | 
			
		||||
[Policy #1]->[<choice> policy #1 allows?]
 | 
			
		||||
[policy #1 allows?] no ->[<state> decision disallowed]
 | 
			
		||||
[policy #1 allows?] yes ->[<state> consult policy #2]
 | 
			
		||||
[consult policy #2]->[Policy #2]
 | 
			
		||||
[Policy #2]->[<choice> policy #2 allows?]
 | 
			
		||||
[policy #2 allows?] no ->[<state> decision disallowed]
 | 
			
		||||
[policy #2 allows?] yes ->[<state> consult policy #3]
 | 
			
		||||
[consult policy #3]->[<state> ...]
 | 
			
		||||
[...]->[<state> consult policy #n]
 | 
			
		||||
[consult policy #n]->[Policy #n]
 | 
			
		||||
[Policy #n]->[<choice> policy #n allows?]
 | 
			
		||||
[policy #n allows?] no ->[<state> decision disallowed]
 | 
			
		||||
[policy #n allows?] yes ->[<state> decision allowed]
 | 
			
		||||
[decision disallowed]->[<end> Disallowed]
 | 
			
		||||
[decision allowed]->[<end> Allowed]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The policy decision is effectively an "and" operation over the individual
 | 
			
		||||
policy decisions: That is, all policies must agree to allow a particular
 | 
			
		||||
policy decision, and the first policy to disallow a decision will cause
 | 
			
		||||
the entire decision to be disallowed. As a consequence of this, policies
 | 
			
		||||
should generally be written with a default behavior of "allow", and
 | 
			
		||||
should only disallow the specific circumstances they are intended to
 | 
			
		||||
disallow.
 | 
			
		||||
 | 
			
		||||
## Type Service
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
[TypeService|
 | 
			
		||||
  listTypes() : Array.<Type>
 | 
			
		||||
  getType(key : string) : Type
 | 
			
		||||
]
 | 
			
		||||
[TypeProvider]--:>[TypeService]
 | 
			
		||||
[TypeProvider]o-[types]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The type service provides metadata about the different types of domain
 | 
			
		||||
objects that exist within an Open MCT Web application. The platform
 | 
			
		||||
implementation reads these types in from extension category `types`
 | 
			
		||||
and wraps them in a JavaScript interface.
 | 
			
		||||
							
								
								
									
										78
									
								
								docs/src/architecture/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								docs/src/architecture/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
# Introduction
 | 
			
		||||
 | 
			
		||||
The purpose of this document is to familiarize developers with the
 | 
			
		||||
overall architecture of Open MCT Web.
 | 
			
		||||
 | 
			
		||||
The target audience includes:
 | 
			
		||||
 | 
			
		||||
* _Platform maintainers_: Individuals involved in developing,
 | 
			
		||||
  extending, and maintaing capabilities of the platform.
 | 
			
		||||
* _Integration developers_: Individuals tasked with integrated
 | 
			
		||||
  Open MCT Web into a larger system, who need to understand
 | 
			
		||||
  its inner workings sufficiently to complete this integration.
 | 
			
		||||
 | 
			
		||||
As the focus of this document is on architecture, whenever possible
 | 
			
		||||
implementation details (such as relevant API or JSON syntax) have been
 | 
			
		||||
omitted. These details may be found in the developer guide.
 | 
			
		||||
 | 
			
		||||
# Overview
 | 
			
		||||
 | 
			
		||||
Open MCT Web is client software: It runs in a web browser and
 | 
			
		||||
provides a user interface, while communicating with various
 | 
			
		||||
server-side resources through browser APIs.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: right
 | 
			
		||||
[Client|[Browser|[Open MCT Web]->[Browser APIs]]]
 | 
			
		||||
[Server|[Web services]]
 | 
			
		||||
[Client]<->[Server]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
While Open MCT Web can be configured to run as a standalone client,
 | 
			
		||||
this is rarely very useful. Instead, it is intended to be used as a
 | 
			
		||||
display and interaction layer for information obtained from a
 | 
			
		||||
variety of back-end services. Doing so requires authoring or utilizing
 | 
			
		||||
adapter plugins which allow Open MCT Web to interact with these services.
 | 
			
		||||
 | 
			
		||||
Typically, the pattern here is to provide a known interface that
 | 
			
		||||
Open MCT Web can utilize, and implement it such that it interacts with
 | 
			
		||||
whatever back-end provides the relevant information.
 | 
			
		||||
Examples of back-ends that can be utilized in this fashion include
 | 
			
		||||
databases for the persistence of user-created objects, or sources of
 | 
			
		||||
telemetry data.
 | 
			
		||||
 | 
			
		||||
## Software Architecture
 | 
			
		||||
 | 
			
		||||
The simplest overview of Open MCT Web is to look at it as a "layered"
 | 
			
		||||
architecture, where each layer more clearly specifies the behavior
 | 
			
		||||
of the software.
 | 
			
		||||
 | 
			
		||||
```nomnoml
 | 
			
		||||
#direction: down
 | 
			
		||||
[Open MCT Web|
 | 
			
		||||
  [Platform]<->[Application]
 | 
			
		||||
  [Framework]->[Application]
 | 
			
		||||
  [Framework]->[Platform]
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
These layers are:
 | 
			
		||||
 | 
			
		||||
* [_Framework_](Framework.md): The framework layer is responsible for
 | 
			
		||||
  managing the interactions between application components. It has no
 | 
			
		||||
  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, feel, and
 | 
			
		||||
  behavior of Open MCT Web. 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.
 | 
			
		||||
* _Application_: The application layer defines specific features of
 | 
			
		||||
  an application built on Open MCT Web. This includes adapters to
 | 
			
		||||
  specific back-ends, new types of things for users to create, and
 | 
			
		||||
  new ways of visualizing objects within the system. This layer
 | 
			
		||||
  typically consists of a mix of custom plug-ins to Open MCT Web,
 | 
			
		||||
  as well as optional features (such as Plot view) included alongside
 | 
			
		||||
  the platform.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								docs/src/guide/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/src/guide/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# Developer Guide
 | 
			
		||||
 | 
			
		||||
This is a placeholder for the developer guide.
 | 
			
		||||
							
								
								
									
										36
									
								
								docs/src/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								docs/src/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
<!--
 | 
			
		||||
 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.
 | 
			
		||||
-->
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head lang="en">
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <title>Open MCT Web Documentation</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body class="user-environ" ng-view>
 | 
			
		||||
    Sections:
 | 
			
		||||
    <ul>
 | 
			
		||||
        <li><a href="api/">API</a></li>
 | 
			
		||||
        <li><a href="guide/">Developer Guide</a></li>
 | 
			
		||||
        <li><a href="architecture/">Architecture Overview</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
                "key": "eventGenerator",
 | 
			
		||||
                "name": "Event Message Generator",
 | 
			
		||||
                "glyph": "f",
 | 
			
		||||
                "description": "An event message generator",
 | 
			
		||||
                "description": "For development use. Creates sample event message data that mimics a live data stream.",
 | 
			
		||||
                "features": "creation",
 | 
			
		||||
                "model": {
 | 
			
		||||
                    "telemetry": {}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Sine Wave Generator",
 | 
			
		||||
    "description": "Example of a component that produces dataa.",
 | 
			
		||||
    "description": "Example of a component that produces data.",
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "components": [
 | 
			
		||||
            {
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
                "key": "generator",
 | 
			
		||||
                "name": "Sine Wave Generator",
 | 
			
		||||
                "glyph": "T",
 | 
			
		||||
                "description": "A sine wave generator",
 | 
			
		||||
                "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
 | 
			
		||||
                "features": "creation",
 | 
			
		||||
                "model": {
 | 
			
		||||
                    "telemetry": {
 | 
			
		||||
@@ -34,6 +34,10 @@
 | 
			
		||||
                        {
 | 
			
		||||
                            "key": "time",
 | 
			
		||||
                            "name": "Time"
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            "key": "yesterday",
 | 
			
		||||
                            "name": "Yesterday"
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    "ranges": [
 | 
			
		||||
@@ -61,4 +65,4 @@
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@
 | 
			
		||||
 * Module defining SinewaveTelemetryProvider. Created by vwoeltje on 11/12/14.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["./SinewaveTelemetry"],
 | 
			
		||||
    function (SinewaveTelemetry) {
 | 
			
		||||
    ["./SinewaveTelemetrySeries"],
 | 
			
		||||
    function (SinewaveTelemetrySeries) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -45,7 +45,7 @@ define(
 | 
			
		||||
            function generateData(request) {
 | 
			
		||||
                return {
 | 
			
		||||
                    key: request.key,
 | 
			
		||||
                    telemetry: new SinewaveTelemetry(request)
 | 
			
		||||
                    telemetry: new SinewaveTelemetrySeries(request)
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -112,4 +112,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return SinewaveTelemetryProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,35 +29,47 @@ define(
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var firstObservedTime = Date.now();
 | 
			
		||||
        var ONE_DAY = 60 * 60 * 24,
 | 
			
		||||
            firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function SinewaveTelemetry(request) {
 | 
			
		||||
            var latestObservedTime = Date.now(),
 | 
			
		||||
                count = Math.floor((latestObservedTime - firstObservedTime) / 1000),
 | 
			
		||||
                period = request.period || 30,
 | 
			
		||||
                generatorData = {};
 | 
			
		||||
        function SinewaveTelemetrySeries(request) {
 | 
			
		||||
            var timeOffset = (request.domain === 'yesterday') ? ONE_DAY : 0,
 | 
			
		||||
                latestTime = Math.floor(Date.now() / 1000) - timeOffset,
 | 
			
		||||
                firstTime = firstObservedTime - timeOffset,
 | 
			
		||||
                endTime = (request.end !== undefined) ?
 | 
			
		||||
                        Math.floor(request.end / 1000) : latestTime,
 | 
			
		||||
                count = Math.min(endTime, latestTime) - firstTime,
 | 
			
		||||
                period = +request.period || 30,
 | 
			
		||||
                generatorData = {},
 | 
			
		||||
                requestStart = (request.start === undefined) ? firstTime :
 | 
			
		||||
                        Math.max(Math.floor(request.start / 1000), firstTime),
 | 
			
		||||
                offset = requestStart - firstTime;
 | 
			
		||||
 | 
			
		||||
            if (request.size !== undefined) {
 | 
			
		||||
                offset = Math.max(offset, count - request.size);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            generatorData.getPointCount = function () {
 | 
			
		||||
                return count;
 | 
			
		||||
                return count - offset;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            generatorData.getDomainValue = function (i, domain) {
 | 
			
		||||
                return i * 1000 +
 | 
			
		||||
                        (domain !== 'delta' ? firstObservedTime : 0);
 | 
			
		||||
                return (i + offset) * 1000 + firstTime * 1000 -
 | 
			
		||||
                    (domain === 'yesterday' ? ONE_DAY : 0);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            generatorData.getRangeValue = function (i, range) {
 | 
			
		||||
                range = range || "sin";
 | 
			
		||||
                return Math[range](i * Math.PI * 2 / period);
 | 
			
		||||
                return Math[range]((i + offset) * Math.PI * 2 / period);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return generatorData;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return SinewaveTelemetry;
 | 
			
		||||
        return SinewaveTelemetrySeries;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										12
									
								
								example/identity/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								example/identity/bundle.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "components": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "ExampleIdentityService.js",
 | 
			
		||||
                "provides": "identityService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "depends": [ "dialogService" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								example/identity/src/ExampleIdentityService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								example/identity/src/ExampleIdentityService.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var DEFAULT_IDENTITY = { key: "user", name: "Example User" },
 | 
			
		||||
            DIALOG_STRUCTURE = {
 | 
			
		||||
                name: "Identify Yourself",
 | 
			
		||||
                sections: [{ rows: [
 | 
			
		||||
                    {
 | 
			
		||||
                        name: "User ID",
 | 
			
		||||
                        control: "textfield",
 | 
			
		||||
                        key: "key",
 | 
			
		||||
                        required: true
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        name: "Human name",
 | 
			
		||||
                        control: "textfield",
 | 
			
		||||
                        key: "name",
 | 
			
		||||
                        required: true
 | 
			
		||||
                    }
 | 
			
		||||
                ]}]
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Example implementation of an identity service. This prompts the
 | 
			
		||||
         * user to enter a name and user ID; in a more realistic
 | 
			
		||||
         * implementation, this would be read from a server, possibly
 | 
			
		||||
         * prompting for a user name and password (or similar) as
 | 
			
		||||
         * appropriate.
 | 
			
		||||
         *
 | 
			
		||||
         * @implements {IdentityService}
 | 
			
		||||
         * @memberof platform/identity
 | 
			
		||||
         */
 | 
			
		||||
        function ExampleIdentityProvider(dialogService) {
 | 
			
		||||
            // Handle rejected dialog messages by treating the
 | 
			
		||||
            // current user as undefined.
 | 
			
		||||
            function echo(v) { return v; }
 | 
			
		||||
            function giveUndefined() { return undefined; }
 | 
			
		||||
 | 
			
		||||
            this.userPromise =
 | 
			
		||||
                dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY)
 | 
			
		||||
                    .then(echo, giveUndefined);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ExampleIdentityProvider.prototype.getUser = function () {
 | 
			
		||||
            return this.userPromise;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ExampleIdentityProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -14,6 +14,7 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "imagery",
 | 
			
		||||
                "name": "Example Imagery",
 | 
			
		||||
                "description": "For development use. Creates example imagery data that mimics a live imagery stream.",
 | 
			
		||||
                "glyph": "T",
 | 
			
		||||
                "features": "creation",
 | 
			
		||||
                "model": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,localStorage*/
 | 
			
		||||
/**
 | 
			
		||||
 * Stubbed implementation of a persistence provider,
 | 
			
		||||
 * to permit objects to be created, saved, etc.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        function BrowserPersistenceProvider($q, SPACE) {
 | 
			
		||||
            var spaces = SPACE ? [SPACE] : [],
 | 
			
		||||
                promises = {
 | 
			
		||||
                    as: function (value) {
 | 
			
		||||
                        return $q.when(value);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                provider;
 | 
			
		||||
 | 
			
		||||
            function setValue(key, value) {
 | 
			
		||||
                localStorage[key] = JSON.stringify(value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getValue(key) {
 | 
			
		||||
                if (localStorage[key]) {
 | 
			
		||||
                    return JSON.parse(localStorage[key]);
 | 
			
		||||
                }
 | 
			
		||||
                return {};
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            provider = {
 | 
			
		||||
                listSpaces: function () {
 | 
			
		||||
                    return promises.as(spaces);
 | 
			
		||||
                },
 | 
			
		||||
                listObjects: function (space) {
 | 
			
		||||
                    var space_obj = getValue(space);
 | 
			
		||||
                    return promises.as(Object.keys(space_obj));
 | 
			
		||||
                },
 | 
			
		||||
                createObject: function (space, key, value) {
 | 
			
		||||
                    var space_obj = getValue(space);
 | 
			
		||||
                    space_obj[key] = value;
 | 
			
		||||
                    setValue(space, space_obj);
 | 
			
		||||
                    return promises.as(true);
 | 
			
		||||
                },
 | 
			
		||||
                readObject: function (space, key) {
 | 
			
		||||
                    var space_obj = getValue(space);
 | 
			
		||||
                    return promises.as(space_obj[key]);
 | 
			
		||||
                },
 | 
			
		||||
                deleteObject: function (space, key, value) {
 | 
			
		||||
                    var space_obj = getValue(space);
 | 
			
		||||
                    delete space_obj[key];
 | 
			
		||||
                    return promises.as(true);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            provider.updateObject = provider.createObject;
 | 
			
		||||
 | 
			
		||||
            return provider;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return BrowserPersistenceProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -4,7 +4,11 @@
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "WatchIndicator.js",
 | 
			
		||||
                "depends": ["$interval", "$rootScope"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "DigestIndicator.js",
 | 
			
		||||
                "depends": ["$interval", "$rootScope"]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								example/profiling/src/DigestIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								example/profiling/src/DigestIndicator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Displays the number of digests that have occurred since the
 | 
			
		||||
         * indicator was first instantiated.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $interval Angular's $interval
 | 
			
		||||
         * @implements {Indicator}
 | 
			
		||||
         */
 | 
			
		||||
        function DigestIndicator($interval, $rootScope) {
 | 
			
		||||
            var digests = 0,
 | 
			
		||||
                displayed = 0,
 | 
			
		||||
                start = Date.now();
 | 
			
		||||
 | 
			
		||||
            function update() {
 | 
			
		||||
                var secs = (Date.now() - start) / 1000;
 | 
			
		||||
                displayed = Math.round(digests / secs);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function increment() {
 | 
			
		||||
                digests += 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $rootScope.$watch(increment);
 | 
			
		||||
 | 
			
		||||
            // Update state every second
 | 
			
		||||
            $interval(update, 1000);
 | 
			
		||||
 | 
			
		||||
            // Provide initial state, too
 | 
			
		||||
            update();
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                getGlyph: function () {
 | 
			
		||||
                    return ".";
 | 
			
		||||
                },
 | 
			
		||||
                getGlyphClass: function () {
 | 
			
		||||
                    return undefined;
 | 
			
		||||
                },
 | 
			
		||||
                getText: function () {
 | 
			
		||||
                    return displayed + " digests/sec";
 | 
			
		||||
                },
 | 
			
		||||
                getDescription: function () {
 | 
			
		||||
                    return "";
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DigestIndicator;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
<html>
 | 
			
		||||
<head lang="en">
 | 
			
		||||
    <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="platform/framework/lib/require.js"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								jsdoc.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								jsdoc.json
									
									
									
									
									
								
							@@ -1,10 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "source": {
 | 
			
		||||
        "include": [
 | 
			
		||||
            "example/",
 | 
			
		||||
            "platform/"
 | 
			
		||||
        ],
 | 
			
		||||
        "includePattern": "(example|platform)/.+\\.js$",
 | 
			
		||||
        "includePattern": "platform/.+\\.js$",
 | 
			
		||||
        "excludePattern": ".+\\Spec\\.js$|lib/.+"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    },
 | 
			
		||||
    "plugins": [
 | 
			
		||||
        "plugins/markdown"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								package.json
									
									
									
									
									
								
							@@ -16,13 +16,22 @@
 | 
			
		||||
    "karma-jasmine": "^0.1.5",
 | 
			
		||||
    "karma-phantomjs-launcher": "^0.1.4",
 | 
			
		||||
    "karma-requirejs": "^0.2.2",
 | 
			
		||||
    "requirejs": "^2.1.17"
 | 
			
		||||
    "requirejs": "^2.1.17",
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "glob": ">= 3.0.0",
 | 
			
		||||
    "split": "^1.0.0",
 | 
			
		||||
    "mkdirp": "^0.5.1",
 | 
			
		||||
    "nomnoml": "^0.0.3",
 | 
			
		||||
    "canvas": "^1.2.7"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node app.js",
 | 
			
		||||
    "test": "karma start --single-run",
 | 
			
		||||
    "jshint": "jshint platform example || exit 0",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -r -d docs"
 | 
			
		||||
    "watch": "karma start",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api",
 | 
			
		||||
    "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs",
 | 
			
		||||
    "docs": "npm run jsdoc ; npm run otherdoc"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,11 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements Open MCT Web's About dialog.
 | 
			
		||||
 * @namespace platform/commonUI/about
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
@@ -29,35 +34,36 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * The AboutController provides information to populate the
 | 
			
		||||
         * About dialog.
 | 
			
		||||
         * @memberof platform/commonUI/about
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {object[]} versions an array of version extensions;
 | 
			
		||||
         *        injected from `versions[]`
 | 
			
		||||
         * @param $window Angular-injected window object
 | 
			
		||||
         */
 | 
			
		||||
        function AboutController(versions, $window) {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get version info. This is given as an array of
 | 
			
		||||
                 * objects, where each object is intended to appear
 | 
			
		||||
                 * as a line-item in the version information listing.
 | 
			
		||||
                 * @memberof AboutController#
 | 
			
		||||
                 * @returns {object[]} version information
 | 
			
		||||
                 */
 | 
			
		||||
                versions: function () {
 | 
			
		||||
                    return versions;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Open a new window (or tab, depending on browser
 | 
			
		||||
                 * configuration) containing open source licenses.
 | 
			
		||||
                 * @memberof AboutController#
 | 
			
		||||
                 */
 | 
			
		||||
                openLicenses: function () {
 | 
			
		||||
                    // Open a new browser window at the licenses route
 | 
			
		||||
                    $window.open("#/licenses");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.versionDefinitions = versions;
 | 
			
		||||
            this.$window = $window;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get version info. This is given as an array of
 | 
			
		||||
         * objects, where each object is intended to appear
 | 
			
		||||
         * as a line-item in the version information listing.
 | 
			
		||||
         * @returns {object[]} version information
 | 
			
		||||
         */
 | 
			
		||||
        AboutController.prototype.versions = function () {
 | 
			
		||||
            return this.versionDefinitions;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Open a new window (or tab, depending on browser
 | 
			
		||||
         * configuration) containing open source licenses.
 | 
			
		||||
         */
 | 
			
		||||
        AboutController.prototype.openLicenses = function () {
 | 
			
		||||
            // Open a new browser window at the licenses route
 | 
			
		||||
            this.$window.open("#/licenses");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return AboutController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,20 +29,22 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides extension-introduced licenses information to the
 | 
			
		||||
         * licenses route.
 | 
			
		||||
         * @memberof platform/commonUI/about
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function LicenseController(licenses) {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get license information.
 | 
			
		||||
                 * @returns {Array} license extensions
 | 
			
		||||
                 */
 | 
			
		||||
                licenses: function () {
 | 
			
		||||
                    return licenses;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.licenseDefinitions = licenses;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get license information.
 | 
			
		||||
         * @returns {Array} license extensions
 | 
			
		||||
         * @memberof platform/commonUI/about.LicenseController#
 | 
			
		||||
         */
 | 
			
		||||
        LicenseController.prototype.licenses = function () {
 | 
			
		||||
            return this.licenseDefinitions;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LicenseController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,21 +29,23 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * The LogoController provides functionality to the application
 | 
			
		||||
         * logo in the bottom-right of the user interface.
 | 
			
		||||
         * @memberof platform/commonUI/about
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param {OverlayService} overlayService the overlay service
 | 
			
		||||
         */
 | 
			
		||||
        function LogoController(overlayService) {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Display the About dialog.
 | 
			
		||||
                 * @memberof LogoController#
 | 
			
		||||
                 */
 | 
			
		||||
                showAboutDialog: function () {
 | 
			
		||||
                    overlayService.createOverlay("overlay-about");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.overlayService = overlayService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Display the About dialog.
 | 
			
		||||
         * @memberof LogoController#
 | 
			
		||||
         * @memberof platform/commonUI/about.LogoController#
 | 
			
		||||
         */
 | 
			
		||||
        LogoController.prototype.showAboutDialog = function () {
 | 
			
		||||
            this.overlayService.createOverlay("overlay-about");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LogoController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,20 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "BrowseController",
 | 
			
		||||
                "implementation": "BrowseController.js",
 | 
			
		||||
                "depends": [ "$scope", "$route", "$location", "objectService", "navigationService", "urlService" ]
 | 
			
		||||
                "depends": [
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    "$route",
 | 
			
		||||
                    "$location",
 | 
			
		||||
                    "objectService",
 | 
			
		||||
                    "navigationService",
 | 
			
		||||
                    "urlService"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "BrowseTreeController",
 | 
			
		||||
                "implementation": "BrowseTreeController.js",
 | 
			
		||||
                "priority": "preferred",
 | 
			
		||||
                "depends": [ "$scope", "agentService" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "BrowseObjectController",
 | 
			
		||||
@@ -25,17 +38,17 @@
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "CreateMenuController",
 | 
			
		||||
                "implementation": "creation/CreateMenuController",
 | 
			
		||||
                "implementation": "creation/CreateMenuController.js",
 | 
			
		||||
                "depends": [ "$scope" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "LocatorController",
 | 
			
		||||
                "implementation": "creation/LocatorController",
 | 
			
		||||
                "depends": [ "$scope" ]
 | 
			
		||||
                "implementation": "creation/LocatorController.js",
 | 
			
		||||
                "depends": [ "$scope", "$timeout" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "MenuArrowController",
 | 
			
		||||
                "implementation": "MenuArrowController",
 | 
			
		||||
                "implementation": "MenuArrowController.js",
 | 
			
		||||
                "depends": [ "$scope" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
@@ -45,12 +58,6 @@
 | 
			
		||||
                "templateUrl": "templates/create/locator.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "topbar-browse",
 | 
			
		||||
                "templateUrl": "templates/topbar-browse.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "representations": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "browse-object",
 | 
			
		||||
@@ -69,8 +76,8 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "grid-item",
 | 
			
		||||
                "templateUrl": "templates/items/grid-item.html",
 | 
			
		||||
                "uses": [ "type", "action" ],
 | 
			
		||||
                "gestures": [ "info","menu" ]
 | 
			
		||||
                "uses": [ "type", "action", "location" ],
 | 
			
		||||
                "gestures": [ "info", "menu" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "object-header",
 | 
			
		||||
@@ -82,18 +89,18 @@
 | 
			
		||||
                "templateUrl": "templates/menu-arrow.html",
 | 
			
		||||
                "uses": [ "action" ],
 | 
			
		||||
                "gestures": [ "menu" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "back-arrow",
 | 
			
		||||
                "uses": [ "context" ],
 | 
			
		||||
                "templateUrl": "templates/back-arrow.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "services": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "navigationService",
 | 
			
		||||
                "implementation": "navigation/NavigationService.js"
 | 
			
		||||
            },        
 | 
			
		||||
            {
 | 
			
		||||
                "key": "creationService",
 | 
			
		||||
                "implementation": "creation/CreationService.js",
 | 
			
		||||
                "depends": [ "persistenceService", "$q", "$log" ]
 | 
			
		||||
            }        
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "actions": [
 | 
			
		||||
            {
 | 
			
		||||
@@ -110,7 +117,7 @@
 | 
			
		||||
                "depends": [ "urlService", "$window" ],
 | 
			
		||||
                "group": "windowing",
 | 
			
		||||
                "glyph": "y",
 | 
			
		||||
			  	"priority": "preferred"
 | 
			
		||||
                "priority": "preferred"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "fullscreen",
 | 
			
		||||
@@ -118,7 +125,7 @@
 | 
			
		||||
                "category": "view-control",
 | 
			
		||||
                "group": "windowing",
 | 
			
		||||
                "glyph": "z",
 | 
			
		||||
			  	"priority": "default"
 | 
			
		||||
                "priority": "default"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "views": [
 | 
			
		||||
@@ -141,6 +148,13 @@
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "creation/CreateActionProvider.js",
 | 
			
		||||
                "depends": [ "typeService", "dialogService", "creationService", "policyService" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "CreationService",
 | 
			
		||||
                "provides": "creationService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "creation/CreationService.js",
 | 
			
		||||
                "depends": [ "persistenceService", "now", "$q", "$log" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "runs": [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								platform/commonUI/browse/res/templates/back-arrow.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								platform/commonUI/browse/res/templates/back-arrow.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
<!--
 | 
			
		||||
 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.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<!-- Back Arrow Icon used on mobile-->
 | 
			
		||||
<a
 | 
			
		||||
   class='type-icon icon ui-symbol s-back'
 | 
			
		||||
   ng-show="context.getPath().length > 2"
 | 
			
		||||
   ng-click="context.getParent().getCapability('action').perform('navigate')">
 | 
			
		||||
    {
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
@@ -20,28 +20,30 @@
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<span ng-controller="BrowseObjectController">
 | 
			
		||||
    <div class="object-browse-bar bar abs">
 | 
			
		||||
        <div class="items-select left abs">
 | 
			
		||||
    <div class="object-browse-bar bar l-flex">
 | 
			
		||||
        <div class="items-select left">
 | 
			
		||||
            <mct-representation key="'back-arrow'"
 | 
			
		||||
                                mct-object="domainObject"
 | 
			
		||||
                                class="l-back"></mct-representation>
 | 
			
		||||
            <mct-representation key="'object-header'" mct-object="domainObject">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="btn-bar right abs">
 | 
			
		||||
            <mct-representation key="'action-group'"
 | 
			
		||||
                                mct-object="domainObject"
 | 
			
		||||
                                parameters="{ category: 'view-control' }">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
 | 
			
		||||
        <div class="btn-bar right">
 | 
			
		||||
            <mct-representation key="'switcher'"
 | 
			
		||||
                                mct-object="domainObject"
 | 
			
		||||
                                ng-model="representation">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
            <!-- Temporarily, on mobile, the action buttons are hidden-->
 | 
			
		||||
            <mct-representation key="'action-group'"
 | 
			
		||||
                                mct-object="domainObject"
 | 
			
		||||
                                parameters="{ category: 'view-control' }"
 | 
			
		||||
                                class="mobile-hide">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class='object-holder abs vscroll'>
 | 
			
		||||
        <mct-representation key="representation.selected.key"
 | 
			
		||||
                            mct-object="representation.selected.key && domainObject">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
    </div>
 | 
			
		||||
    <mct-representation key="representation.selected.key"
 | 
			
		||||
                        mct-object="representation.selected.key && domainObject"
 | 
			
		||||
                        class="abs object-holder">
 | 
			
		||||
    </mct-representation>
 | 
			
		||||
</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,30 +19,51 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div content="jquery-wrapper" class="abs holder-all browse-mode">
 | 
			
		||||
 | 
			
		||||
<div class="abs holder-all browse-mode" ng-controller="BrowseController">
 | 
			
		||||
    <mct-include key="'topbar-browse'"></mct-include>
 | 
			
		||||
    <div class="holder browse-area s-browse-area abs" ng-controller="BrowseController">
 | 
			
		||||
    <div class="holder browse-area s-browse-area abs browse-wrapper"
 | 
			
		||||
         ng-controller="BrowseTreeController as tree"
 | 
			
		||||
         ng-class="tree.visible() ? 'browse-showtree' : 'browse-hidetree'">
 | 
			
		||||
        <mct-split-pane class='contents abs' anchor='left'>
 | 
			
		||||
            <div
 | 
			
		||||
                class='split-pane-component treeview pane left'
 | 
			
		||||
                >
 | 
			
		||||
                <mct-representation key="'create-button'" mct-object="navigatedObject">
 | 
			
		||||
                </mct-representation>
 | 
			
		||||
                <div class='holder tree-holder abs'>
 | 
			
		||||
                    <mct-representation key="'tree'"
 | 
			
		||||
                                        mct-object="domainObject"
 | 
			
		||||
                                        ng-model="treeModel">
 | 
			
		||||
            <div class='split-pane-component treeview pane left'>
 | 
			
		||||
                <div class="holder abs l-mobile">
 | 
			
		||||
                    <mct-representation key="'create-button'"
 | 
			
		||||
                                        mct-object="navigatedObject"
 | 
			
		||||
                                        mct-device="desktop">
 | 
			
		||||
                    </mct-representation>
 | 
			
		||||
                    <div class='holder search-holder abs'
 | 
			
		||||
                         ng-class="{active: treeModel.search}">
 | 
			
		||||
                        <mct-representation key="'search'"
 | 
			
		||||
                                     mct-object="domainObject"
 | 
			
		||||
                                     ng-model="treeModel">
 | 
			
		||||
                        </mct-representation>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class='tree-holder abs mobile-tree-holder'
 | 
			
		||||
                         ng-hide="treeModel.search">
 | 
			
		||||
                        <mct-representation key="'tree'"
 | 
			
		||||
                                            mct-object="domainObject"
 | 
			
		||||
                                            parameters="tree"
 | 
			
		||||
                                            ng-model="treeModel">
 | 
			
		||||
                        </mct-representation>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-splitter></mct-splitter>
 | 
			
		||||
            <div class='split-pane-component items pane'>
 | 
			
		||||
                <div class='holder abs' id='content-area'>
 | 
			
		||||
                    <mct-representation mct-object="navigatedObject" key="'browse-object'">
 | 
			
		||||
 | 
			
		||||
            <mct-splitter class="mobile-hide"></mct-splitter>
 | 
			
		||||
 | 
			
		||||
            <div class='split-pane-component items pane right-repr'>
 | 
			
		||||
                <div class='holder abs l-mobile' id='content-area'>
 | 
			
		||||
                    <mct-representation mct-object="navigatedObject"
 | 
			
		||||
                                        key="'browse-object'">
 | 
			
		||||
                    </mct-representation>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="key-properties ui-symbol icon mobile-menu-icon desktop-hide"
 | 
			
		||||
                     mct-device="mobile"
 | 
			
		||||
                     ng-click="tree.toggle()">m</div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </mct-split-pane>
 | 
			
		||||
    </div>
 | 
			
		||||
    <mct-include key="'bottombar'"></mct-include>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,11 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class='object-header'>
 | 
			
		||||
    <span class="label s-label">
 | 
			
		||||
        <span class='type-icon icon ui-symbol'>{{type.getGlyph()}}</span>
 | 
			
		||||
<div class='object-header object-header-mobile'>
 | 
			
		||||
    <span class='type-icon ui-symbol'>{{type.getGlyph()}}</span>
 | 
			
		||||
    <!--span class='type-name mobile-important-hide'>{{type.getName()}}</span-->
 | 
			
		||||
    <span class="l-elem-wrapper l-flex">
 | 
			
		||||
        <span ng-if="parameters.mode" class='action'>{{parameters.mode}}</span>
 | 
			
		||||
        <span class='type-name'>{{type.getName()}}</span>
 | 
			
		||||
        <span class='title-label'>{{model.name}}</span>
 | 
			
		||||
        <mct-representation key="'menu-arrow'" mct-object='domainObject'></mct-representation>
 | 
			
		||||
    </span>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,10 +20,11 @@
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
 | 
			
		||||
    <div class="btn btn-menu create-btn major" ng-click="createController.toggle()">
 | 
			
		||||
	    Create
 | 
			
		||||
    <div class="s-menu-btn major create-btn" ng-click="createController.toggle()">
 | 
			
		||||
		<span class="ui-symbol icon type-icon">+</span>
 | 
			
		||||
		<span class="title-label">Create</span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="menu dropdown super-menu" ng-show="createController.isActive()">
 | 
			
		||||
    <div class="menu super-menu" ng-show="createController.isActive()">
 | 
			
		||||
        <mct-representation mct-object="domainObject" key="'create-menu'">
 | 
			
		||||
        </mct-representation>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,8 @@
 | 
			
		||||
    <div class="pane left menu-items">
 | 
			
		||||
        <ul>
 | 
			
		||||
 | 
			
		||||
            <li ng-repeat="createAction in createActions">
 | 
			
		||||
            <li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
 | 
			
		||||
                <a
 | 
			
		||||
                   ng-click="createAction.perform()"
 | 
			
		||||
                   ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
 | 
			
		||||
                   ng-mouseleave="representation.activeMetadata = undefined">
 | 
			
		||||
                    <span class="ui-symbol icon type-icon">
 | 
			
		||||
@@ -48,4 +47,4 @@
 | 
			
		||||
            {{representation.activeMetadata.description}}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div ng-controller="LocatorController" class="selector-list">
 | 
			
		||||
    <div>
 | 
			
		||||
    <div class="wrapper">
 | 
			
		||||
        <mct-representation key="'tree'"
 | 
			
		||||
                            mct-object="rootObject"
 | 
			
		||||
                            ng-model="treeModel">
 | 
			
		||||
 
 | 
			
		||||
@@ -21,27 +21,29 @@
 | 
			
		||||
-->
 | 
			
		||||
<!-- For selected, add class 'selected' to outer div -->
 | 
			
		||||
<div class='item grid-item' ng-click='action.perform("navigate")'>
 | 
			
		||||
    <div class="contents abs">
 | 
			
		||||
    <div class='contents abs'>
 | 
			
		||||
        <div class='top-bar bar abs'>
 | 
			
		||||
            <div class='left abs'>
 | 
			
		||||
                <mct-include key="_checkbox"></mct-include>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class='right abs'>
 | 
			
		||||
                <div class='ui-symbol icon alert hidden' onclick="alert('Not yet functional. When this is visible, it means that this object needs to be updated. Clicking will allow that action via a dialog.');">!</div>
 | 
			
		||||
                <div class='ui-symbol icon profile' title="Shared">P</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class='ui-symbol profile' title='Shared'>O</div>
 | 
			
		||||
            <mct-representation class="desktop-hide" key="'info-button'" mct-object="domainObject"></mct-representation>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class='item-main abs'>
 | 
			
		||||
            <div class='ui-symbol icon lg abs item-type'>{{type.getGlyph()}}</div>
 | 
			
		||||
            <div class='ui-symbol icon abs item-open'>}</div>
 | 
			
		||||
            <div class='ui-symbol icon lg item-type'>
 | 
			
		||||
                {{type.getGlyph()}}
 | 
			
		||||
                <span
 | 
			
		||||
                    class="ui-symbol l-icon-link" title="This object is a link"
 | 
			
		||||
                    ng-show="location.isLink()"
 | 
			
		||||
                    ></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class='ui-symbol abs item-open'>}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class='bottom-bar bar abs'>
 | 
			
		||||
            <div class='title'>{{model.name}}</div>
 | 
			
		||||
            <div class='details'>
 | 
			
		||||
                <span>{{type.getName()}}</span>
 | 
			
		||||
                <span ng-show="model.composition !== undefined">
 | 
			
		||||
                    {{model.composition.length}} Items
 | 
			
		||||
                    - {{model.composition.length}} Item<span ng-show="model.composition.length > 1">s</span>
 | 
			
		||||
                </span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
 http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 | 
			
		||||
 Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 License for the specific language governing permissions and limitations
 | 
			
		||||
 under the License.
 | 
			
		||||
 | 
			
		||||
 Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class='top-bar browse abs'>
 | 
			
		||||
    <!-- TO-DO: replace JS placeholders for click actions -->
 | 
			
		||||
    <div class='browse-main bar abs left'>
 | 
			
		||||
        <a class="menu-element btn btn-menu browse-btn" onclick="alert('Not yet functional. This will allow filtering of browsed objects and search context.');">
 | 
			
		||||
            <span class='ui-symbol badge major' href=''>*</span>Browse All<span class='ui-symbol invoke-menu'>v</span>
 | 
			
		||||
        </a>
 | 
			
		||||
        <input type='text' class='control filter' name='filter-available'/>
 | 
			
		||||
        <a class='icon icon-filter ui-symbol' onclick="alert('Not yet functional. This will initiate a search.');">M</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class='icon-buttons-main bar abs right'>
 | 
			
		||||
        <a class='ui-symbol icon major alert' onclick="alert('Not yet functional. This will allow updating of domain objects that need to be refreshed.');">!<span id='alert-actions-menu' class='ui-symbol invoke-menu'>v</span></a>
 | 
			
		||||
        <!--a class='ui-symbol icon major profile' href=''>P<span id='profile-actions-menu' class='ui-symbol invoke-menu'>v</span></a-->
 | 
			
		||||
        <a class='ui-symbol icon major settings' onclick="alert('Not yet functional. This will allow access to application configuration settings.');">G<span id='settings-actions-menu' class='ui-symbol invoke-menu'>v</span></a>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -22,7 +22,8 @@
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining BrowseController. Created by vwoeltje on 11/7/14.
 | 
			
		||||
 * This bundle implements Browse mode.
 | 
			
		||||
 * @namespace platform/commonUI/browse
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
@@ -39,6 +40,7 @@ define(
 | 
			
		||||
         * which Angular templates first have access to the domain object
 | 
			
		||||
         * hierarchy.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function BrowseController($scope, $route, $location, objectService, navigationService, urlService) {
 | 
			
		||||
@@ -53,7 +55,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                unlisten = $scope.$on('$locationChangeSuccess', function () {
 | 
			
		||||
                    // Checks path to make sure /browse/ is at front
 | 
			
		||||
                    // if so, change $route.current                    
 | 
			
		||||
                    // if so, change $route.current
 | 
			
		||||
                    if ($location.path().indexOf("/browse/") === 0) {
 | 
			
		||||
                        $route.current = priorRoute;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -63,7 +65,7 @@ define(
 | 
			
		||||
                // path to new, addressed, path based on
 | 
			
		||||
                // domainObject
 | 
			
		||||
                $location.path(urlService.urlForLocation("browse", domainObject));
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Callback for updating the in-scope reference to the object
 | 
			
		||||
@@ -157,3 +159,4 @@ define(
 | 
			
		||||
        return BrowseController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller for the `browse-object` representation of a domain
 | 
			
		||||
         * object (the right-hand side of Browse mode.)
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function BrowseObjectController($scope, $location, $route) {
 | 
			
		||||
@@ -71,3 +72,4 @@ define(
 | 
			
		||||
        return BrowseObjectController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								platform/commonUI/browse/src/BrowseTreeController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								platform/commonUI/browse/src/BrowseTreeController.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller to provide the ability to show/hide the tree in
 | 
			
		||||
         * Browse mode.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         */
 | 
			
		||||
        function BrowseTreeController($scope, agentService) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
            this.agentService = agentService;
 | 
			
		||||
            this.state = true;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Callback to invoke when any selection occurs in the tree.
 | 
			
		||||
             * This controller can be passed in as the `parameters` object
 | 
			
		||||
             * to the tree representation.
 | 
			
		||||
             *
 | 
			
		||||
             * @property {Function} callback
 | 
			
		||||
             * @memberof platform/commonUI/browse.BrowseTreeController#
 | 
			
		||||
             */
 | 
			
		||||
            this.callback = function () {
 | 
			
		||||
                // Note that, since this is a callback to pass, this is not
 | 
			
		||||
                // declared as a method but as a property which happens to
 | 
			
		||||
                // be a function.
 | 
			
		||||
                if (agentService.isPhone() && agentService.isPortrait()) {
 | 
			
		||||
                    // On phones, trees should collapse in portrait mode
 | 
			
		||||
                    // when something is navigated-to.
 | 
			
		||||
                    self.state = false;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Toggle the visibility of the tree.
 | 
			
		||||
         */
 | 
			
		||||
        BrowseTreeController.prototype.toggle = function () {
 | 
			
		||||
            this.state = !this.state;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the desired visibility state of the tree.
 | 
			
		||||
         * @returns {boolean} true when visible
 | 
			
		||||
         */
 | 
			
		||||
        BrowseTreeController.prototype.visible = function () {
 | 
			
		||||
            return this.state;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return BrowseTreeController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -33,19 +33,29 @@ define(
 | 
			
		||||
         * A left-click on the menu arrow should display a 
 | 
			
		||||
         * context menu. This controller launches the context 
 | 
			
		||||
         * menu. 
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function MenuArrowController($scope) {
 | 
			
		||||
            function showMenu(event) {
 | 
			
		||||
                var actionContext = {key: 'menu', domainObject: $scope.domainObject, event: event};
 | 
			
		||||
                $scope.domainObject.getCapability('action').perform(actionContext);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return {
 | 
			
		||||
                showMenu: showMenu
 | 
			
		||||
            };
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Show a context menu for the domain object in this scope.
 | 
			
		||||
         *
 | 
			
		||||
         * @param event the browser event which caused this (used to
 | 
			
		||||
         *        position the menu)
 | 
			
		||||
         */
 | 
			
		||||
        MenuArrowController.prototype.showMenu = function (event) {
 | 
			
		||||
            var actionContext = {
 | 
			
		||||
                key: 'menu',
 | 
			
		||||
                domainObject: this.$scope.domainObject,
 | 
			
		||||
                event: event
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$scope.domainObject.getCapability('action').perform(actionContext);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return MenuArrowController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,10 @@ define(
 | 
			
		||||
         * domain objects of a specific type. This is the action that
 | 
			
		||||
         * is performed when a user uses the Create menu.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         *
 | 
			
		||||
         * @param {Type} type the type of domain object to create
 | 
			
		||||
         * @param {DomainObject} parent the domain object that should
 | 
			
		||||
         *        act as a container for the newly-created object
 | 
			
		||||
@@ -49,78 +52,84 @@ define(
 | 
			
		||||
         *        of the newly-created domain object
 | 
			
		||||
         */
 | 
			
		||||
        function CreateAction(type, parent, context, dialogService, creationService, policyService) {
 | 
			
		||||
            this.metadata = {
 | 
			
		||||
                key: 'create',
 | 
			
		||||
                glyph: type.getGlyph(),
 | 
			
		||||
                name: type.getName(),
 | 
			
		||||
                type: type.getKey(),
 | 
			
		||||
                description: type.getDescription(),
 | 
			
		||||
                context: context
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.type = type;
 | 
			
		||||
            this.parent = parent;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
            this.dialogService = dialogService;
 | 
			
		||||
            this.creationService = creationService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create a new object of the given type.
 | 
			
		||||
         * This will prompt for user input first.
 | 
			
		||||
         */
 | 
			
		||||
        CreateAction.prototype.perform = function () {
 | 
			
		||||
            /*
 | 
			
		||||
             Overview of steps in object creation:
 | 
			
		||||
 | 
			
		||||
             1. Show dialog
 | 
			
		||||
               a. Prepare dialog contents
 | 
			
		||||
               b. Invoke dialogService
 | 
			
		||||
             a. Prepare dialog contents
 | 
			
		||||
             b. Invoke dialogService
 | 
			
		||||
             2. Create new object in persistence service
 | 
			
		||||
               a. Generate UUID
 | 
			
		||||
               b. Store model
 | 
			
		||||
             a. Generate UUID
 | 
			
		||||
             b. Store model
 | 
			
		||||
             3. Mutate destination container
 | 
			
		||||
               a. Get mutation capability
 | 
			
		||||
               b. Add new id to composition
 | 
			
		||||
             a. Get mutation capability
 | 
			
		||||
             b. Add new id to composition
 | 
			
		||||
             4. Persist destination container
 | 
			
		||||
               a. ...use persistence capability.
 | 
			
		||||
             a. ...use persistence capability.
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            function perform() {
 | 
			
		||||
                // The wizard will handle creating the form model based
 | 
			
		||||
                // on the type...
 | 
			
		||||
                var wizard = new CreateWizard(type, parent, policyService);
 | 
			
		||||
            // The wizard will handle creating the form model based
 | 
			
		||||
            // on the type...
 | 
			
		||||
            var wizard =
 | 
			
		||||
                new CreateWizard(this.type, this.parent, this.policyService),
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
                // Create and persist the new object, based on user
 | 
			
		||||
                // input.
 | 
			
		||||
                function persistResult(formValue) {
 | 
			
		||||
                    var parent = wizard.getLocation(formValue),
 | 
			
		||||
                        newModel = wizard.createModel(formValue);
 | 
			
		||||
                    return creationService.createObject(newModel, parent);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function doNothing() {
 | 
			
		||||
                    // Create cancelled, do nothing
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return dialogService.getUserInput(
 | 
			
		||||
                    wizard.getFormStructure(),
 | 
			
		||||
                    wizard.getInitialFormValue()
 | 
			
		||||
                ).then(persistResult, doNothing);
 | 
			
		||||
            // Create and persist the new object, based on user
 | 
			
		||||
            // input.
 | 
			
		||||
            function persistResult(formValue) {
 | 
			
		||||
                var parent = wizard.getLocation(formValue),
 | 
			
		||||
                    newModel = wizard.createModel(formValue);
 | 
			
		||||
                return self.creationService.createObject(newModel, parent);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Create a new object of the given type.
 | 
			
		||||
                 * This will prompt for user input first.
 | 
			
		||||
                 * @method
 | 
			
		||||
                 * @memberof CreateAction
 | 
			
		||||
                 */
 | 
			
		||||
                perform: perform,
 | 
			
		||||
            function doNothing() {
 | 
			
		||||
                // Create cancelled, do nothing
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get metadata about this action. This includes fields:
 | 
			
		||||
                 * * `name`: Human-readable name
 | 
			
		||||
                 * * `key`: Machine-readable identifier ("create")
 | 
			
		||||
                 * * `glyph`: Glyph to use as an icon for this action
 | 
			
		||||
                 * * `description`: Human-readable description
 | 
			
		||||
                 * * `context`: The context in which this action will be performed.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @return {object} metadata about the create action
 | 
			
		||||
                 */
 | 
			
		||||
                getMetadata: function () {
 | 
			
		||||
                    return {
 | 
			
		||||
                        key: 'create',
 | 
			
		||||
                        glyph: type.getGlyph(),
 | 
			
		||||
                        name: type.getName(),
 | 
			
		||||
                        type: type.getKey(),
 | 
			
		||||
                        description: type.getDescription(),
 | 
			
		||||
                        context: context
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return this.dialogService.getUserInput(
 | 
			
		||||
                wizard.getFormStructure(),
 | 
			
		||||
                wizard.getInitialFormValue()
 | 
			
		||||
            ).then(persistResult, doNothing);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Metadata associated with a Create action.
 | 
			
		||||
         * @typedef {ActionMetadata} CreateActionMetadata
 | 
			
		||||
         * @property {string} type the key for the type of domain object
 | 
			
		||||
         *           to be created
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get metadata about this action.
 | 
			
		||||
         * @returns {CreateActionMetadata} metadata about this action
 | 
			
		||||
         */
 | 
			
		||||
        CreateAction.prototype.getMetadata = function () {
 | 
			
		||||
           return this.metadata;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CreateAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,10 @@ define(
 | 
			
		||||
         * The CreateActionProvider is an ActionProvider which introduces
 | 
			
		||||
         * a Create action for each creatable domain object type.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {ActionService}
 | 
			
		||||
         *
 | 
			
		||||
         * @param {TypeService} typeService the type service, used to discover
 | 
			
		||||
         *        available types
 | 
			
		||||
         * @param {DialogService} dialogService the dialog service, used by
 | 
			
		||||
@@ -44,44 +47,41 @@ define(
 | 
			
		||||
         *        object creation.
 | 
			
		||||
         */
 | 
			
		||||
        function CreateActionProvider(typeService, dialogService, creationService, policyService) {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get all Create actions which are applicable in the provided
 | 
			
		||||
                 * context.
 | 
			
		||||
                 * @memberof CreateActionProvider
 | 
			
		||||
                 * @method
 | 
			
		||||
                 * @returns {CreateAction[]}
 | 
			
		||||
                 */
 | 
			
		||||
                getActions: function (actionContext) {
 | 
			
		||||
                    var context = actionContext || {},
 | 
			
		||||
                        key = context.key,
 | 
			
		||||
                        destination = context.domainObject;
 | 
			
		||||
 | 
			
		||||
                    // We only provide Create actions, and we need a
 | 
			
		||||
                    // domain object to serve as the container for the
 | 
			
		||||
                    // newly-created object (although the user may later
 | 
			
		||||
                    // make a different selection)
 | 
			
		||||
                    if (key !== 'create' || !destination) {
 | 
			
		||||
                        return [];
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Introduce one create action per type
 | 
			
		||||
                    return typeService.listTypes().filter(function (type) {
 | 
			
		||||
                        return type.hasFeature("creation");
 | 
			
		||||
                    }).map(function (type) {
 | 
			
		||||
                        return new CreateAction(
 | 
			
		||||
                            type,
 | 
			
		||||
                            destination,
 | 
			
		||||
                            context,
 | 
			
		||||
                            dialogService,
 | 
			
		||||
                            creationService,
 | 
			
		||||
                            policyService
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.typeService = typeService;
 | 
			
		||||
            this.dialogService = dialogService;
 | 
			
		||||
            this.creationService = creationService;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CreateActionProvider.prototype.getActions = function (actionContext) {
 | 
			
		||||
            var context = actionContext || {},
 | 
			
		||||
                key = context.key,
 | 
			
		||||
                destination = context.domainObject,
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            // We only provide Create actions, and we need a
 | 
			
		||||
            // domain object to serve as the container for the
 | 
			
		||||
            // newly-created object (although the user may later
 | 
			
		||||
            // make a different selection)
 | 
			
		||||
            if (key !== 'create' || !destination) {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Introduce one create action per type
 | 
			
		||||
            return this.typeService.listTypes().filter(function (type) {
 | 
			
		||||
                return type.hasFeature("creation");
 | 
			
		||||
            }).map(function (type) {
 | 
			
		||||
                return new CreateAction(
 | 
			
		||||
                    type,
 | 
			
		||||
                    destination,
 | 
			
		||||
                    context,
 | 
			
		||||
                    self.dialogService,
 | 
			
		||||
                    self.creationService,
 | 
			
		||||
                    self.policyService
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CreateActionProvider;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ define(
 | 
			
		||||
         * set of Create actions based on the currently-selected
 | 
			
		||||
         * domain object.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function CreateMenuController($scope) {
 | 
			
		||||
@@ -55,4 +56,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return CreateMenuController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,6 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Defines the CreateWizard, used by the CreateAction to
 | 
			
		||||
 * populate the form shown in dialog based on the created type.
 | 
			
		||||
 *
 | 
			
		||||
 * @module core/action/create-wizard
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
@@ -37,16 +31,33 @@ define(
 | 
			
		||||
         * @param {TypeImpl} type the type of domain object to be created
 | 
			
		||||
         * @param {DomainObject} parent the domain object to serve as
 | 
			
		||||
         *        the initial parent for the created object, in the dialog
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof module:core/action/create-wizard
 | 
			
		||||
         */
 | 
			
		||||
        function CreateWizard(type, parent, policyService) {
 | 
			
		||||
            var model = type.getInitialModel(),
 | 
			
		||||
                properties = type.getProperties();
 | 
			
		||||
            this.type = type;
 | 
			
		||||
            this.model = type.getInitialModel();
 | 
			
		||||
            this.properties = type.getProperties();
 | 
			
		||||
            this.parent = parent;
 | 
			
		||||
            this.policyService = policyService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the form model for this wizard; this is a description
 | 
			
		||||
         * that will be rendered to an HTML form. See the
 | 
			
		||||
         * platform/forms bundle
 | 
			
		||||
         *
 | 
			
		||||
         * @return {FormModel} formModel the form model to
 | 
			
		||||
         *         show in the create dialog
 | 
			
		||||
         */
 | 
			
		||||
        CreateWizard.prototype.getFormStructure = function () {
 | 
			
		||||
            var sections = [],
 | 
			
		||||
                type = this.type,
 | 
			
		||||
                policyService = this.policyService;
 | 
			
		||||
 | 
			
		||||
            function validateLocation(locatingObject) {
 | 
			
		||||
                var locatingType = locatingObject &&
 | 
			
		||||
                        locatingObject.getCapability('type');
 | 
			
		||||
                    locatingObject.getCapability('type');
 | 
			
		||||
                return locatingType && policyService.allow(
 | 
			
		||||
                    "composition",
 | 
			
		||||
                    locatingType,
 | 
			
		||||
@@ -54,96 +65,87 @@ define(
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sections.push({
 | 
			
		||||
                name: "Properties",
 | 
			
		||||
                rows: this.properties.map(function (property, index) {
 | 
			
		||||
                    // Property definition is same as form row definition
 | 
			
		||||
                    var row = Object.create(property.getDefinition());
 | 
			
		||||
 | 
			
		||||
                    // Use index as the key into the formValue;
 | 
			
		||||
                    // this correlates to the indexing provided by
 | 
			
		||||
                    // getInitialFormValue
 | 
			
		||||
                    row.key = index;
 | 
			
		||||
 | 
			
		||||
                    return row;
 | 
			
		||||
                }).filter(function (row) {
 | 
			
		||||
                    // Only show rows which have defined controls
 | 
			
		||||
                    return row && row.control;
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Ensure there is always a "save in" section
 | 
			
		||||
            sections.push({ name: 'Location', rows: [{
 | 
			
		||||
                name: "Save In",
 | 
			
		||||
                control: "locator",
 | 
			
		||||
                validate: validateLocation,
 | 
			
		||||
                key: "createParent"
 | 
			
		||||
            }]});
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the form model for this wizard; this is a description
 | 
			
		||||
                 * that will be rendered to an HTML form. See the
 | 
			
		||||
                 * platform/forms bundle
 | 
			
		||||
                 *
 | 
			
		||||
                 * @return {FormModel} formModel the form model to
 | 
			
		||||
                 *         show in the create dialog
 | 
			
		||||
                 */
 | 
			
		||||
                getFormStructure: function () {
 | 
			
		||||
                    var sections = [];
 | 
			
		||||
 | 
			
		||||
                    sections.push({
 | 
			
		||||
                        name: "Properties",
 | 
			
		||||
                        rows: properties.map(function (property, index) {
 | 
			
		||||
                            // Property definition is same as form row definition
 | 
			
		||||
                            var row = Object.create(property.getDefinition());
 | 
			
		||||
 | 
			
		||||
                            // Use index as the key into the formValue;
 | 
			
		||||
                            // this correlates to the indexing provided by
 | 
			
		||||
                            // getInitialFormValue
 | 
			
		||||
                            row.key = index;
 | 
			
		||||
 | 
			
		||||
                            return row;
 | 
			
		||||
                        })
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    // Ensure there is always a "save in" section
 | 
			
		||||
                    sections.push({ name: 'Location', rows: [{
 | 
			
		||||
                        name: "Save In",
 | 
			
		||||
                        control: "locator",
 | 
			
		||||
                        validate: validateLocation,
 | 
			
		||||
                        key: "createParent"
 | 
			
		||||
                    }]});
 | 
			
		||||
 | 
			
		||||
                    return {
 | 
			
		||||
                        sections: sections,
 | 
			
		||||
                        name: "Create a New " + type.getName()
 | 
			
		||||
                    };
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the initial value for the form being described.
 | 
			
		||||
                 * This will include the values for all properties described
 | 
			
		||||
                 * in the structure.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @returns {object} the initial value of the form
 | 
			
		||||
                 */
 | 
			
		||||
                getInitialFormValue: function () {
 | 
			
		||||
                    // Start with initial values for properties
 | 
			
		||||
                    var formValue = properties.map(function (property) {
 | 
			
		||||
                        return property.getValue(model);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    // Include the createParent
 | 
			
		||||
                    formValue.createParent = parent;
 | 
			
		||||
 | 
			
		||||
                    return formValue;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Based on a populated form, get the domain object which
 | 
			
		||||
                 * should be used as a parent for the newly-created object.
 | 
			
		||||
                 * @return {DomainObject}
 | 
			
		||||
                 */
 | 
			
		||||
                getLocation: function (formValue) {
 | 
			
		||||
                    return formValue.createParent || parent;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Create the domain object model for a newly-created object,
 | 
			
		||||
                 * based on user input read from a formModel.
 | 
			
		||||
                 * @return {object} the domain object' model
 | 
			
		||||
                 */
 | 
			
		||||
                createModel: function (formValue) {
 | 
			
		||||
                    // Clone
 | 
			
		||||
                    var newModel = JSON.parse(JSON.stringify(model));
 | 
			
		||||
 | 
			
		||||
                    // Always use the type from the type definition
 | 
			
		||||
                    newModel.type = type.getKey();
 | 
			
		||||
 | 
			
		||||
                    // Update all properties
 | 
			
		||||
                    properties.forEach(function (property, index) {
 | 
			
		||||
                        property.setValue(newModel, formValue[index]);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    return newModel;
 | 
			
		||||
                }
 | 
			
		||||
                sections: sections,
 | 
			
		||||
                name: "Create a New " + this.type.getName()
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the initial value for the form being described.
 | 
			
		||||
         * This will include the values for all properties described
 | 
			
		||||
         * in the structure.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {object} the initial value of the form
 | 
			
		||||
         */
 | 
			
		||||
        CreateWizard.prototype.getInitialFormValue = function () {
 | 
			
		||||
            // Start with initial values for properties
 | 
			
		||||
            var model = this.model,
 | 
			
		||||
                formValue = this.properties.map(function (property) {
 | 
			
		||||
                    return property.getValue(model);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
            // Include the createParent
 | 
			
		||||
            formValue.createParent = this.parent;
 | 
			
		||||
 | 
			
		||||
            return formValue;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Based on a populated form, get the domain object which
 | 
			
		||||
         * should be used as a parent for the newly-created object.
 | 
			
		||||
         * @return {DomainObject}
 | 
			
		||||
         */
 | 
			
		||||
        CreateWizard.prototype.getLocation = function (formValue) {
 | 
			
		||||
            return formValue.createParent || this.parent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create the domain object model for a newly-created object,
 | 
			
		||||
         * based on user input read from a formModel.
 | 
			
		||||
         * @return {object} the domain object model
 | 
			
		||||
         */
 | 
			
		||||
        CreateWizard.prototype.createModel = function (formValue) {
 | 
			
		||||
            // Clone
 | 
			
		||||
            var newModel = JSON.parse(JSON.stringify(this.model));
 | 
			
		||||
 | 
			
		||||
            // Always use the type from the type definition
 | 
			
		||||
            newModel.type = this.type.getKey();
 | 
			
		||||
 | 
			
		||||
            // Update all properties
 | 
			
		||||
            this.properties.forEach(function (property, index) {
 | 
			
		||||
                property.setValue(newModel, formValue[index]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return newModel;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CreateWizard;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,15 +39,44 @@ define(
 | 
			
		||||
         * persisting new domain objects. Handles all actual object
 | 
			
		||||
         * mutation and persistence associated with domain object
 | 
			
		||||
         * creation.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function CreationService(persistenceService, $q, $log) {
 | 
			
		||||
        function CreationService(persistenceService, now, $q, $log) {
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.$log = $log;
 | 
			
		||||
            this.now = now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create a new domain object with the provided model, as
 | 
			
		||||
         * a member of the provided parent domain object's composition.
 | 
			
		||||
         * This parent will additionally determine which persistence
 | 
			
		||||
         * space an object is created within (as it is possible to
 | 
			
		||||
         * have multiple persistence spaces attached.)
 | 
			
		||||
         *
 | 
			
		||||
         * Note that the model passed in for object creation may be
 | 
			
		||||
         * modified to attach additional initial properties associated
 | 
			
		||||
         * with domain object creation.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {object} model the model for the newly-created
 | 
			
		||||
         *        domain object
 | 
			
		||||
         * @param {DomainObject} parent the domain object which
 | 
			
		||||
         *        should contain the newly-created domain object
 | 
			
		||||
         *        in its composition
 | 
			
		||||
         * @return {Promise} a promise that will resolve when the domain
 | 
			
		||||
         *         object has been created
 | 
			
		||||
         */
 | 
			
		||||
        CreationService.prototype.createObject = function (model, parent) {
 | 
			
		||||
            var persistence = parent.getCapability("persistence"),
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            // Persist the new domain object's model; it will be fully
 | 
			
		||||
            // constituted as a domain object when loaded back, as all
 | 
			
		||||
            // domain object models are.
 | 
			
		||||
            function doPersist(space, id, model) {
 | 
			
		||||
                return persistenceService.createObject(
 | 
			
		||||
                return self.persistenceService.createObject(
 | 
			
		||||
                    space,
 | 
			
		||||
                    id,
 | 
			
		||||
                    model
 | 
			
		||||
@@ -58,84 +87,44 @@ define(
 | 
			
		||||
            // composition, so that it will subsequently appear
 | 
			
		||||
            // as a child contained by that parent.
 | 
			
		||||
            function addToComposition(id, parent, parentPersistence) {
 | 
			
		||||
                var mutatationResult = parent.useCapability("mutation", function (model) {
 | 
			
		||||
                    if (Array.isArray(model.composition)) {
 | 
			
		||||
                        // Don't add if the id is already there
 | 
			
		||||
                        if (model.composition.indexOf(id) === -1) {
 | 
			
		||||
                            model.composition.push(id);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // This is abnormal; composition should be an array
 | 
			
		||||
                        $log.warn(NO_COMPOSITION_WARNING + parent.getId());
 | 
			
		||||
                        return false; // Cancel mutation
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                var compositionCapability = parent.getCapability('composition'),
 | 
			
		||||
                    addResult = compositionCapability &&
 | 
			
		||||
                        compositionCapability.add(id);
 | 
			
		||||
 | 
			
		||||
                return $q.when(mutatationResult).then(function (result) {
 | 
			
		||||
                return self.$q.when(addResult).then(function (result) {
 | 
			
		||||
                    if (!result) {
 | 
			
		||||
                        $log.error("Could not mutate " + parent.getId());
 | 
			
		||||
                        self.$log.error("Could not modify " + parent.getId());
 | 
			
		||||
                        return undefined;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return parentPersistence.persist().then(function () {
 | 
			
		||||
                        // Locate and return new Object in context of parent.
 | 
			
		||||
                        return parent
 | 
			
		||||
                            .useCapability('composition')
 | 
			
		||||
                            .then(function (children) {
 | 
			
		||||
                                var i;
 | 
			
		||||
                                for (i = 0; i < children.length; i += 1) {
 | 
			
		||||
                                    if (children[i].getId() === id) {
 | 
			
		||||
                                        return children[i];
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                        return result;
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Create a new domain object with the provided model as a
 | 
			
		||||
            // member of the specified parent's composition
 | 
			
		||||
            function createObject(model, parent) {
 | 
			
		||||
                var persistence = parent.getCapability("persistence");
 | 
			
		||||
 | 
			
		||||
                // We need the parent's persistence capability to determine
 | 
			
		||||
                // what space to create the new object's model in.
 | 
			
		||||
                if (!persistence) {
 | 
			
		||||
                    $log.warn(NON_PERSISTENT_WARNING);
 | 
			
		||||
                    return $q.reject(new Error(NON_PERSISTENT_WARNING));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // We create a new domain object in three sequential steps:
 | 
			
		||||
                // 1. Get a new UUID for the object
 | 
			
		||||
                // 2. Create a model with that ID in the persistence space
 | 
			
		||||
                // 3. Add that ID to
 | 
			
		||||
                return $q.when(
 | 
			
		||||
                    uuid()
 | 
			
		||||
                ).then(function (id) {
 | 
			
		||||
                    return doPersist(persistence.getSpace(), id, model);
 | 
			
		||||
                }).then(function (id) {
 | 
			
		||||
                    return addToComposition(id, parent, persistence);
 | 
			
		||||
                });
 | 
			
		||||
            // We need the parent's persistence capability to determine
 | 
			
		||||
            // what space to create the new object's model in.
 | 
			
		||||
            if (!persistence) {
 | 
			
		||||
                self.$log.warn(NON_PERSISTENT_WARNING);
 | 
			
		||||
                return self.$q.reject(new Error(NON_PERSISTENT_WARNING));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Create a new domain object with the provided model, as
 | 
			
		||||
                 * a member of the provided parent domain object's composition.
 | 
			
		||||
                 * This parent will additionally determine which persistence
 | 
			
		||||
                 * space an object is created within (as it is possible to
 | 
			
		||||
                 * have multiple persistence spaces attached.)
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {object} model the model for the newly-created
 | 
			
		||||
                 *        domain object
 | 
			
		||||
                 * @param {DomainObject} parent the domain object which
 | 
			
		||||
                 *        should contain the newly-created domain object
 | 
			
		||||
                 *        in its composition
 | 
			
		||||
                 */
 | 
			
		||||
                createObject: createObject
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            // We create a new domain object in three sequential steps:
 | 
			
		||||
            // 1. Get a new UUID for the object
 | 
			
		||||
            // 2. Create a model with that ID in the persistence space
 | 
			
		||||
            // 3. Add that ID to
 | 
			
		||||
            return self.$q.when(uuid()).then(function (id) {
 | 
			
		||||
                model.persisted = self.now();
 | 
			
		||||
                return doPersist(persistence.getSpace(), id, model);
 | 
			
		||||
            }).then(function (id) {
 | 
			
		||||
                return addToComposition(id, parent, persistence);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return CreationService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,9 +30,10 @@ define(
 | 
			
		||||
         * Controller for the "locator" control, which provides the
 | 
			
		||||
         * user with the ability to select a domain object as the
 | 
			
		||||
         * destination for a newly-created object in the Create menu.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function LocatorController($scope) {
 | 
			
		||||
        function LocatorController($scope, $timeout) {
 | 
			
		||||
            // Populate values needed by the locator control. These are:
 | 
			
		||||
            // * rootObject: The top-level object, since we want to show
 | 
			
		||||
            //               the full tree
 | 
			
		||||
@@ -40,9 +41,19 @@ define(
 | 
			
		||||
            //              used for bi-directional object selection.
 | 
			
		||||
            function setLocatingObject(domainObject, priorObject) {
 | 
			
		||||
                var context = domainObject &&
 | 
			
		||||
                    domainObject.getCapability("context");
 | 
			
		||||
                    domainObject.getCapability("context"),
 | 
			
		||||
                    contextRoot = context && context.getRoot();
 | 
			
		||||
 | 
			
		||||
                if (contextRoot && contextRoot !== $scope.rootObject) {
 | 
			
		||||
                    $scope.rootObject = undefined;
 | 
			
		||||
                    // Update the displayed tree on a timeout to avoid
 | 
			
		||||
                    // an infinite digest exception.
 | 
			
		||||
                    $timeout(function () {
 | 
			
		||||
                        $scope.rootObject =
 | 
			
		||||
                            (context && context.getRoot()) || $scope.rootObject;
 | 
			
		||||
                    }, 0);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $scope.rootObject = (context && context.getRoot()) || $scope.rootObject;
 | 
			
		||||
                $scope.treeModel.selectedObject = domainObject;
 | 
			
		||||
                $scope.ngModel[$scope.field] = domainObject;
 | 
			
		||||
 | 
			
		||||
@@ -51,10 +62,7 @@ define(
 | 
			
		||||
                        $scope.structure &&
 | 
			
		||||
                            $scope.structure.validate) {
 | 
			
		||||
                    if (!$scope.structure.validate(domainObject)) {
 | 
			
		||||
                        setLocatingObject(
 | 
			
		||||
                            $scope.structure.validate(priorObject) ?
 | 
			
		||||
                                    priorObject : undefined
 | 
			
		||||
                        );
 | 
			
		||||
                        setLocatingObject(priorObject, undefined);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -79,3 +87,4 @@ define(
 | 
			
		||||
        return LocatorController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,32 +31,34 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The navigate action navigates to a specific domain object.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function NavigateAction(navigationService, $q, context) {
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
 | 
			
		||||
            function perform() {
 | 
			
		||||
                // Set navigation, and wrap like a promise
 | 
			
		||||
                return $q.when(navigationService.setNavigation(domainObject));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Navigate to the object described in the context.
 | 
			
		||||
                 * @returns {Promise} a promise that is resolved once the
 | 
			
		||||
                 *          navigation has been updated
 | 
			
		||||
                 */
 | 
			
		||||
                perform: perform
 | 
			
		||||
            };
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.navigationService = navigationService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Navigate to the object described in the context.
 | 
			
		||||
         * @returns {Promise} a promise that is resolved once the
 | 
			
		||||
         *          navigation has been updated
 | 
			
		||||
         */
 | 
			
		||||
        NavigateAction.prototype.perform = function () {
 | 
			
		||||
            // Set navigation, and wrap like a promise
 | 
			
		||||
            return this.$q.when(
 | 
			
		||||
                this.navigationService.setNavigation(this.domainObject)
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Navigate as an action is only applicable when a domain object
 | 
			
		||||
         * is described in the action context.
 | 
			
		||||
         * @param {ActionContext} context the context in which the action
 | 
			
		||||
         *        will be performed
 | 
			
		||||
         * @returns true if applicable
 | 
			
		||||
         * @returns {boolean} true if applicable
 | 
			
		||||
         */
 | 
			
		||||
        NavigateAction.appliesTo = function (context) {
 | 
			
		||||
            return context.domainObject !== undefined;
 | 
			
		||||
@@ -64,4 +66,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return NavigateAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -32,68 +32,58 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * The navigation service maintains the application's current
 | 
			
		||||
         * navigation state, and allows listening for changes thereto.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function NavigationService() {
 | 
			
		||||
            var navigated,
 | 
			
		||||
                callbacks = [];
 | 
			
		||||
            this.navigated = undefined;
 | 
			
		||||
            this.callbacks = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Getter for current navigation
 | 
			
		||||
            function getNavigation() {
 | 
			
		||||
                return navigated;
 | 
			
		||||
            }
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the current navigation state.
 | 
			
		||||
         * @returns {DomainObject} the object that is navigated-to
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.getNavigation = function () {
 | 
			
		||||
            return this.navigated;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            // Setter for navigation; invokes callbacks
 | 
			
		||||
            function setNavigation(value) {
 | 
			
		||||
                if (navigated !== value) {
 | 
			
		||||
                    navigated = value;
 | 
			
		||||
                    callbacks.forEach(function (callback) {
 | 
			
		||||
                        callback(value);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Adds a callback
 | 
			
		||||
            function addListener(callback) {
 | 
			
		||||
                callbacks.push(callback);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Filters out a callback
 | 
			
		||||
            function removeListener(callback) {
 | 
			
		||||
                callbacks = callbacks.filter(function (cb) {
 | 
			
		||||
                    return cb !== callback;
 | 
			
		||||
        /**
 | 
			
		||||
         * Set the current navigation state. This will invoke listeners.
 | 
			
		||||
         * @param {DomainObject} domainObject the domain object to navigate to
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.setNavigation = function (value) {
 | 
			
		||||
            if (this.navigated !== value) {
 | 
			
		||||
                this.navigated = value;
 | 
			
		||||
                this.callbacks.forEach(function (callback) {
 | 
			
		||||
                    callback(value);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the current navigation state.
 | 
			
		||||
                 */
 | 
			
		||||
                getNavigation: getNavigation,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Set the current navigation state. Thiswill invoke listeners.
 | 
			
		||||
                 * @param {DomainObject} value the domain object to navigate
 | 
			
		||||
                 *        to
 | 
			
		||||
                 */
 | 
			
		||||
                setNavigation: setNavigation,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Listen for changes in navigation. The passed callback will
 | 
			
		||||
                 * be invoked with the new domain object of navigation when
 | 
			
		||||
                 * this changes.
 | 
			
		||||
                 * @param {function} callback the callback to invoke when
 | 
			
		||||
                 *        navigation state changes
 | 
			
		||||
                 */
 | 
			
		||||
                addListener: addListener,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Stop listening for changes in navigation state.
 | 
			
		||||
                 * @param {function} callback the callback which should
 | 
			
		||||
                 *        no longer be invoked when navigation state
 | 
			
		||||
                 *        changes
 | 
			
		||||
                 */
 | 
			
		||||
                removeListener: removeListener
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        /**
 | 
			
		||||
         * Listen for changes in navigation. The passed callback will
 | 
			
		||||
         * be invoked with the new domain object of navigation when
 | 
			
		||||
         * this changes.
 | 
			
		||||
         * @param {function} callback the callback to invoke when
 | 
			
		||||
         *        navigation state changes
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.addListener = function (callback) {
 | 
			
		||||
            this.callbacks.push(callback);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Stop listening for changes in navigation state.
 | 
			
		||||
         * @param {function} callback the callback which should
 | 
			
		||||
         *        no longer be invoked when navigation state
 | 
			
		||||
         *        changes
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.removeListener = function (callback) {
 | 
			
		||||
            this.callbacks = this.callbacks.filter(function (cb) {
 | 
			
		||||
                return cb !== callback;
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NavigationService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,36 +35,32 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * The fullscreen action toggles between fullscreen display
 | 
			
		||||
         * and regular in-window display.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function FullscreenAction(context) {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Toggle full screen state
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    screenfull.toggle();
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get metadata about this action, including the
 | 
			
		||||
                 * applicable glyph to display.
 | 
			
		||||
                 */
 | 
			
		||||
                getMetadata: function () {
 | 
			
		||||
                    // We override getMetadata, because the glyph 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.description = screenfull.isFullscreen ?
 | 
			
		||||
                            EXIT_FULLSCREEN : ENTER_FULLSCREEN;
 | 
			
		||||
                    metadata.group = "windowing";
 | 
			
		||||
                    metadata.context = context;
 | 
			
		||||
                    return metadata;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.context = context;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FullscreenAction.prototype.perform = function () {
 | 
			
		||||
            screenfull.toggle();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        FullscreenAction.prototype.getMetadata = function () {
 | 
			
		||||
            // We override getMetadata, because the glyph 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.description = screenfull.isFullscreen ?
 | 
			
		||||
                EXIT_FULLSCREEN : ENTER_FULLSCREEN;
 | 
			
		||||
            metadata.group = "windowing";
 | 
			
		||||
            metadata.context = this.context;
 | 
			
		||||
            return metadata;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return FullscreenAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,35 +33,29 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * The new tab action allows a domain object to be opened
 | 
			
		||||
         * into a new browser tab.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function NewTabAction(urlService, $window, context) {
 | 
			
		||||
            // Returns the selected domain object
 | 
			
		||||
            // when using the context menu or the top right button
 | 
			
		||||
            // based on the context and the existance of the object
 | 
			
		||||
            // It is set to object an returned
 | 
			
		||||
            function getSelectedObject() {
 | 
			
		||||
                var object;
 | 
			
		||||
                if (context.selectedObject) {
 | 
			
		||||
                    object = context.selectedObject;
 | 
			
		||||
                } else {
 | 
			
		||||
                    object = context.domainObject;
 | 
			
		||||
                }
 | 
			
		||||
                return object;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return {
 | 
			
		||||
                // Performs the open in new tab function
 | 
			
		||||
                // By calling the url service, the mode needed
 | 
			
		||||
                // (browse) and the domainObject is passed in and
 | 
			
		||||
                // the path is returned and opened in a new tab
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    $window.open(urlService.urlForNewTab("browse", getSelectedObject()),
 | 
			
		||||
                                "_blank");
 | 
			
		||||
                }
 | 
			
		||||
            context = context || {};
 | 
			
		||||
 | 
			
		||||
            this.urlService = urlService;
 | 
			
		||||
            this.open = function () {
 | 
			
		||||
                $window.open.apply($window, arguments);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Choose the object to be opened into a new tab
 | 
			
		||||
            this.domainObject = context.selectedObject || context.domainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NewTabAction.prototype.perform = function () {
 | 
			
		||||
            this.open(
 | 
			
		||||
                this.urlService.urlForNewTab("browse", this.domainObject),
 | 
			
		||||
                "_blank"
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NewTabAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ define(
 | 
			
		||||
        /**
 | 
			
		||||
         * Updates the title of the current window to reflect the name
 | 
			
		||||
         * of the currently navigated-to domain object.
 | 
			
		||||
         * @memberof platform/commonUI/browse
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function WindowTitler(navigationService, $rootScope, $document) {
 | 
			
		||||
@@ -49,4 +50,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return WindowTitler;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -146,12 +146,14 @@ define(
 | 
			
		||||
                expect(mockScope.navigatedObject).toEqual(mockDomainObject);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("releases its navigation listener when its scope is destroyed", function () {
 | 
			
		||||
                expect(mockScope.$on).toHaveBeenCalledWith(
 | 
			
		||||
                    "$destroy",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1]();
 | 
			
		||||
 | 
			
		||||
                // Should remove the listener it added earlier
 | 
			
		||||
                expect(mockNavigationService.removeListener).toHaveBeenCalledWith(
 | 
			
		||||
                    mockNavigationService.addListener.mostRecentCall.args[0]
 | 
			
		||||
@@ -222,16 +224,16 @@ define(
 | 
			
		||||
                mockNavigationService.addListener.mostRecentCall.args[0](
 | 
			
		||||
                    mockNextObject
 | 
			
		||||
                );
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                // Allows the path index to be checked
 | 
			
		||||
                // prior to setting $route.current                
 | 
			
		||||
                // prior to setting $route.current
 | 
			
		||||
                mockLocation.path.andReturn("/browse/");
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                // Exercise the Angular workaround
 | 
			
		||||
                mockScope.$on.mostRecentCall.args[1]();
 | 
			
		||||
                expect(mockUnlisten).toHaveBeenCalled();
 | 
			
		||||
                
 | 
			
		||||
                // location.path to be called with the urlService's 
 | 
			
		||||
 | 
			
		||||
                // location.path to be called with the urlService's
 | 
			
		||||
                // urlFor function with the next domainObject and mode
 | 
			
		||||
                expect(mockLocation.path).toHaveBeenCalledWith(
 | 
			
		||||
                    mockUrlService.urlForLocation(mockMode, mockNextObject)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								platform/commonUI/browse/test/BrowseTreeControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								platform/commonUI/browse/test/BrowseTreeControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/BrowseTreeController"],
 | 
			
		||||
    function (BrowseTreeController) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("The BrowseTreeController", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockAgentService,
 | 
			
		||||
                mockDomainObjects,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            // We want to reinstantiate for each test case
 | 
			
		||||
            // because device state can influence constructor-time behavior
 | 
			
		||||
            function instantiateController() {
 | 
			
		||||
                return new BrowseTreeController(
 | 
			
		||||
                    mockScope,
 | 
			
		||||
                    mockAgentService
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockScope = jasmine.createSpyObj("$scope", [ "$on" ]);
 | 
			
		||||
                mockDomainObjects = ['a', 'b'].map(function (id) {
 | 
			
		||||
                    var mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                        'domainObject-' + id,
 | 
			
		||||
                        [ 'getId', 'getModel', 'getCapability' ]
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    mockDomainObject.getId.andReturn(id);
 | 
			
		||||
                    mockDomainObject.getModel.andReturn({});
 | 
			
		||||
 | 
			
		||||
                    return mockDomainObject;
 | 
			
		||||
                });
 | 
			
		||||
                mockAgentService = jasmine.createSpyObj(
 | 
			
		||||
                    "agentService",
 | 
			
		||||
                    [ "isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape" ]
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is initially visible", function () {
 | 
			
		||||
                expect(instantiateController().visible()).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows visibility to be toggled", function () {
 | 
			
		||||
                controller = instantiateController();
 | 
			
		||||
                controller.toggle();
 | 
			
		||||
                expect(controller.visible()).toBeFalsy();
 | 
			
		||||
                controller.toggle();
 | 
			
		||||
                expect(controller.visible()).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("collapses on navigation changes on portrait-oriented phones", function () {
 | 
			
		||||
                mockAgentService.isMobile.andReturn(true);
 | 
			
		||||
                mockAgentService.isPhone.andReturn(true);
 | 
			
		||||
                mockAgentService.isPortrait.andReturn(true);
 | 
			
		||||
                controller = instantiateController();
 | 
			
		||||
                expect(controller.visible()).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
                // Simulate a change from the tree by invoking controller's
 | 
			
		||||
                controller.callback();
 | 
			
		||||
 | 
			
		||||
                // Tree should have collapsed
 | 
			
		||||
                expect(controller.visible()).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -42,7 +42,9 @@ define(
 | 
			
		||||
                    "property" + name,
 | 
			
		||||
                    [ "getDefinition", "getValue", "setValue" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockProperty.getDefinition.andReturn({});
 | 
			
		||||
                mockProperty.getDefinition.andReturn({
 | 
			
		||||
                    control: "textfield"
 | 
			
		||||
                });
 | 
			
		||||
                mockProperty.getValue.andReturn(name);
 | 
			
		||||
                return mockProperty;
 | 
			
		||||
            }
 | 
			
		||||
@@ -157,4 +159,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The creation service", function () {
 | 
			
		||||
            var mockPersistenceService,
 | 
			
		||||
                mockNow,
 | 
			
		||||
                mockQ,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockParentObject,
 | 
			
		||||
@@ -38,6 +39,7 @@ define(
 | 
			
		||||
                mockMutationCapability,
 | 
			
		||||
                mockPersistenceCapability,
 | 
			
		||||
                mockCompositionCapability,
 | 
			
		||||
                mockContextCapability,
 | 
			
		||||
                mockCapabilities,
 | 
			
		||||
                creationService;
 | 
			
		||||
 | 
			
		||||
@@ -62,6 +64,7 @@ define(
 | 
			
		||||
                    "persistenceService",
 | 
			
		||||
                    [ "createObject" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockNow = jasmine.createSpy('now');
 | 
			
		||||
                mockQ = { when: mockPromise, reject: mockReject };
 | 
			
		||||
                mockLog = jasmine.createSpyObj(
 | 
			
		||||
                    "$log",
 | 
			
		||||
@@ -85,24 +88,32 @@ define(
 | 
			
		||||
                );
 | 
			
		||||
                mockCompositionCapability = jasmine.createSpyObj(
 | 
			
		||||
                    "composition",
 | 
			
		||||
                    ["invoke"]
 | 
			
		||||
                    ["invoke", "add"]
 | 
			
		||||
                );
 | 
			
		||||
                mockContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    "context",
 | 
			
		||||
                    ["getPath"]
 | 
			
		||||
                );
 | 
			
		||||
                mockCapabilities = {
 | 
			
		||||
                    mutation: mockMutationCapability,
 | 
			
		||||
                    persistence: mockPersistenceCapability,
 | 
			
		||||
                    composition: mockCompositionCapability
 | 
			
		||||
                    composition: mockCompositionCapability,
 | 
			
		||||
                    context: mockContextCapability
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockPersistenceService.createObject.andReturn(
 | 
			
		||||
                    mockPromise(true)
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockNow.andReturn(12321);
 | 
			
		||||
 | 
			
		||||
                mockParentObject.getCapability.andCallFake(function (key) {
 | 
			
		||||
                    return mockCapabilities[key];
 | 
			
		||||
                });
 | 
			
		||||
                mockParentObject.useCapability.andCallFake(function (key, value) {
 | 
			
		||||
                    return mockCapabilities[key].invoke(value);
 | 
			
		||||
                });
 | 
			
		||||
                mockParentObject.getId.andReturn('parentId');
 | 
			
		||||
 | 
			
		||||
                mockPersistenceCapability.persist.andReturn(
 | 
			
		||||
                    mockPromise(true)
 | 
			
		||||
@@ -113,9 +124,11 @@ define(
 | 
			
		||||
                mockCompositionCapability.invoke.andReturn(
 | 
			
		||||
                    mockPromise([mockNewObject])
 | 
			
		||||
                );
 | 
			
		||||
                mockCompositionCapability.add.andReturn(mockPromise(true));
 | 
			
		||||
 | 
			
		||||
                creationService = new CreationService(
 | 
			
		||||
                    mockPersistenceService,
 | 
			
		||||
                    mockNow,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockLog
 | 
			
		||||
                );
 | 
			
		||||
@@ -136,33 +149,34 @@ define(
 | 
			
		||||
                    parentModel = { composition: ["notAnyUUID"] };
 | 
			
		||||
                creationService.createObject(model, mockParentObject);
 | 
			
		||||
 | 
			
		||||
                // Invoke the mutation callback
 | 
			
		||||
                expect(mockMutationCapability.invoke).toHaveBeenCalled();
 | 
			
		||||
                mockMutationCapability.invoke.mostRecentCall.args[0](parentModel);
 | 
			
		||||
 | 
			
		||||
                // Should have a longer composition now, with the new UUID
 | 
			
		||||
                expect(parentModel.composition.length).toEqual(2);
 | 
			
		||||
                // Verify that a new ID was added
 | 
			
		||||
                expect(mockCompositionCapability.add)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(String));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("warns if parent has no composition", function () {
 | 
			
		||||
                var model = { someKey: "some value" },
 | 
			
		||||
                    parentModel = { };
 | 
			
		||||
                creationService.createObject(model, mockParentObject);
 | 
			
		||||
            it("provides the newly-created object", function () {
 | 
			
		||||
                var mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                        'newDomainObject',
 | 
			
		||||
                        ['getId', 'getModel', 'getCapability']
 | 
			
		||||
                    ),
 | 
			
		||||
                    mockCallback = jasmine.createSpy('callback');
 | 
			
		||||
 | 
			
		||||
                // Verify precondition; no prior warnings
 | 
			
		||||
                expect(mockLog.warn).not.toHaveBeenCalled();
 | 
			
		||||
                // Act as if the object had been created
 | 
			
		||||
                mockCompositionCapability.add.andCallFake(function (id) {
 | 
			
		||||
                    mockDomainObject.getId.andReturn(id);
 | 
			
		||||
                    mockCompositionCapability.invoke
 | 
			
		||||
                        .andReturn(mockPromise([mockDomainObject]));
 | 
			
		||||
                    return mockPromise(mockDomainObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Invoke the mutation callback
 | 
			
		||||
                expect(mockMutationCapability.invoke).toHaveBeenCalled();
 | 
			
		||||
                mockMutationCapability.invoke.mostRecentCall.args[0](parentModel);
 | 
			
		||||
                // Should find it in the composition
 | 
			
		||||
                creationService.createObject({}, mockParentObject)
 | 
			
		||||
                    .then(mockCallback);
 | 
			
		||||
 | 
			
		||||
                expect(mockCallback).toHaveBeenCalledWith(mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                // Should have a longer composition now, with the new UUID
 | 
			
		||||
                expect(mockLog.warn).toHaveBeenCalled();
 | 
			
		||||
                // Composition should still be undefined
 | 
			
		||||
                expect(parentModel.composition).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("warns if parent has no persistence capability", function () {
 | 
			
		||||
                // Callbacks
 | 
			
		||||
                var success = jasmine.createSpy("success"),
 | 
			
		||||
@@ -178,7 +192,6 @@ define(
 | 
			
		||||
                expect(mockLog.warn).toHaveBeenCalled();
 | 
			
		||||
                expect(success).not.toHaveBeenCalled();
 | 
			
		||||
                expect(failure).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("logs an error when mutaton fails", function () {
 | 
			
		||||
@@ -187,14 +200,19 @@ define(
 | 
			
		||||
                var model = { someKey: "some value" },
 | 
			
		||||
                    parentModel = { composition: ["notAnyUUID"] };
 | 
			
		||||
 | 
			
		||||
                mockMutationCapability.invoke.andReturn(mockPromise(false));
 | 
			
		||||
                mockCompositionCapability.add.andReturn(mockPromise(false));
 | 
			
		||||
 | 
			
		||||
                creationService.createObject(model, mockParentObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockLog.error).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("attaches a 'persisted' timestamp", function () {
 | 
			
		||||
                var model = { someKey: "some value" };
 | 
			
		||||
                creationService.createObject(model, mockParentObject);
 | 
			
		||||
                expect(model.persisted).toEqual(mockNow());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The locator controller", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockTimeout,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockRootObject,
 | 
			
		||||
                mockContext,
 | 
			
		||||
@@ -41,6 +42,7 @@ define(
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    [ "$watch" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockTimeout = jasmine.createSpy("$timeout");
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getCapability" ]
 | 
			
		||||
@@ -60,7 +62,7 @@ define(
 | 
			
		||||
                mockScope.ngModel = {};
 | 
			
		||||
                mockScope.field = "someField";
 | 
			
		||||
 | 
			
		||||
                controller = new LocatorController(mockScope);
 | 
			
		||||
                controller = new LocatorController(mockScope, mockTimeout);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("adds a treeModel to scope", function () {
 | 
			
		||||
@@ -80,6 +82,7 @@ define(
 | 
			
		||||
                // Need to pass on selection changes as updates to
 | 
			
		||||
                // the control's value
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockScope.ngModel.someField).toEqual(mockDomainObject);
 | 
			
		||||
                expect(mockScope.rootObject).toEqual(mockRootObject);
 | 
			
		||||
 | 
			
		||||
@@ -95,6 +98,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                // Pass selection change
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.structure.validate).toHaveBeenCalled();
 | 
			
		||||
                // Change should have been rejected
 | 
			
		||||
@@ -108,14 +112,16 @@ define(
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockScope.ngModelController.$setValidity)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(String), true);
 | 
			
		||||
 | 
			
		||||
                mockScope.$watch.mostRecentCall.args[1](undefined);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockScope.ngModelController.$setValidity)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(String), false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
[
 | 
			
		||||
    "BrowseController",
 | 
			
		||||
    "BrowseObjectController",
 | 
			
		||||
    "BrowseTreeController",
 | 
			
		||||
    "MenuArrowController",
 | 
			
		||||
    "creation/CreateAction",
 | 
			
		||||
    "creation/CreateActionProvider",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
        All fields marked <span class="ui-symbol req">*</span> are required.
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs form outline editor">
 | 
			
		||||
<div class="abs form editor">
 | 
			
		||||
    <div class='abs contents l-dialog'>
 | 
			
		||||
        <mct-form ng-model="ngModel.value"
 | 
			
		||||
                  structure="ngModel.structure"
 | 
			
		||||
@@ -34,13 +34,13 @@
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs bottom-bar">
 | 
			
		||||
    <a class='btn lg major'
 | 
			
		||||
       href=''
 | 
			
		||||
    <a class='s-btn major'
 | 
			
		||||
       ng-class="{ disabled: !createForm.$valid }"
 | 
			
		||||
       ng-click="ngModel.confirm()">
 | 
			
		||||
        OK
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class='btn lg subtle' href='' ng-click="ngModel.cancel()">
 | 
			
		||||
    <a class='s-btn'
 | 
			
		||||
       ng-click="ngModel.cancel()">
 | 
			
		||||
        Cancel
 | 
			
		||||
    </a>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -35,7 +35,7 @@
 | 
			
		||||
    <div class="abs bottom-bar">
 | 
			
		||||
        <a ng-repeat="option in ngModel.dialog.options"
 | 
			
		||||
           href=''
 | 
			
		||||
           class="btn lg"
 | 
			
		||||
           class="s-btn lg"
 | 
			
		||||
           title="{{option.description}}"
 | 
			
		||||
           ng-click="ngModel.confirm(option.key)"
 | 
			
		||||
           ng-class="{ major: $first, subtle: !$first }">
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
        <a href=""
 | 
			
		||||
           ng-click="ngModel.cancel()"
 | 
			
		||||
           ng-if="ngModel.cancel"
 | 
			
		||||
           class="btn normal ui-symbol close">
 | 
			
		||||
           class="clk-icon icon ui-symbol close">
 | 
			
		||||
            x
 | 
			
		||||
        </a>
 | 
			
		||||
        <div class="abs contents" ng-transclude>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,9 @@
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining DialogService. Created by vwoeltje on 11/10/14.
 | 
			
		||||
 * This bundle implements the dialog service, which can be used to
 | 
			
		||||
 * launch dialogs for user input & notifications.
 | 
			
		||||
 * @namespace platform/commonUI/dialog
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
@@ -32,128 +34,130 @@ define(
 | 
			
		||||
         * The dialog service is responsible for handling window-modal
 | 
			
		||||
         * communication with the user, such as displaying forms for user
 | 
			
		||||
         * input.
 | 
			
		||||
         * @memberof platform/commonUI/dialog
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function DialogService(overlayService, $q, $log) {
 | 
			
		||||
            var overlay,
 | 
			
		||||
                dialogVisible = false;
 | 
			
		||||
 | 
			
		||||
            // Stop showing whatever overlay is currently active
 | 
			
		||||
            // (e.g. because the user hit cancel)
 | 
			
		||||
            function dismiss() {
 | 
			
		||||
                if (overlay) {
 | 
			
		||||
                    overlay.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
                dialogVisible = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getDialogResponse(key, model, resultGetter) {
 | 
			
		||||
                // We will return this result as a promise, because user
 | 
			
		||||
                // input is asynchronous.
 | 
			
		||||
                var deferred = $q.defer(),
 | 
			
		||||
                    overlayModel;
 | 
			
		||||
 | 
			
		||||
                // Confirm function; this will be passed in to the
 | 
			
		||||
                // overlay-dialog template and associated with a
 | 
			
		||||
                // OK button click
 | 
			
		||||
                function confirm(value) {
 | 
			
		||||
                    // Pass along the result
 | 
			
		||||
                    deferred.resolve(resultGetter ? resultGetter() : value);
 | 
			
		||||
 | 
			
		||||
                    // Stop showing the dialog
 | 
			
		||||
                    dismiss();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Cancel function; this will be passed in to the
 | 
			
		||||
                // overlay-dialog template and associated with a
 | 
			
		||||
                // Cancel or X button click
 | 
			
		||||
                function cancel() {
 | 
			
		||||
                    deferred.reject();
 | 
			
		||||
                    dismiss();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Add confirm/cancel callbacks
 | 
			
		||||
                model.confirm = confirm;
 | 
			
		||||
                model.cancel = cancel;
 | 
			
		||||
 | 
			
		||||
                if (dialogVisible) {
 | 
			
		||||
                    // Only one dialog should be shown at a time.
 | 
			
		||||
                    // The application design should be such that
 | 
			
		||||
                    // we never even try to do this.
 | 
			
		||||
                    $log.warn([
 | 
			
		||||
                        "Dialog already showing; ",
 | 
			
		||||
                        "unable to show ",
 | 
			
		||||
                        model.name
 | 
			
		||||
                    ].join(""));
 | 
			
		||||
                    deferred.reject();
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Add the overlay using the OverlayService, which
 | 
			
		||||
                    // will handle actual insertion into the DOM
 | 
			
		||||
                    overlay = overlayService.createOverlay(
 | 
			
		||||
                        key,
 | 
			
		||||
                        model
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    // Track that a dialog is already visible, to
 | 
			
		||||
                    // avoid spawning multiple dialogs at once.
 | 
			
		||||
                    dialogVisible = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return deferred.promise;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getUserInput(formModel, value) {
 | 
			
		||||
                var overlayModel = {
 | 
			
		||||
                        title: formModel.name,
 | 
			
		||||
                        message: formModel.message,
 | 
			
		||||
                        structure: formModel,
 | 
			
		||||
                        value: value
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                // Provide result from the model
 | 
			
		||||
                function resultGetter() {
 | 
			
		||||
                    return overlayModel.value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Show the overlay-dialog
 | 
			
		||||
                return getDialogResponse(
 | 
			
		||||
                    "overlay-dialog",
 | 
			
		||||
                    overlayModel,
 | 
			
		||||
                    resultGetter
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getUserChoice(dialogModel) {
 | 
			
		||||
                // Show the overlay-options dialog
 | 
			
		||||
                return getDialogResponse(
 | 
			
		||||
                    "overlay-options",
 | 
			
		||||
                    { dialog: dialogModel }
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Request user input via a window-modal dialog.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {FormModel} formModel a description of the form
 | 
			
		||||
                 *        to be shown (see platform/forms)
 | 
			
		||||
                 * @param {object} value the initial state of the form
 | 
			
		||||
                 * @returns {Promise} a promsie for the form value that the
 | 
			
		||||
                 *          user has supplied; this may be rejected if
 | 
			
		||||
                 *          user input cannot be obtained (for instance,
 | 
			
		||||
                 *          because the user cancelled the dialog)
 | 
			
		||||
                 */
 | 
			
		||||
                getUserInput: getUserInput,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Request that the user chooses from a set of options,
 | 
			
		||||
                 * which will be shown as buttons.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param dialogModel a description of the dialog to show
 | 
			
		||||
                 */
 | 
			
		||||
                getUserChoice: getUserChoice
 | 
			
		||||
            };
 | 
			
		||||
            this.overlayService = overlayService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.$log = $log;
 | 
			
		||||
            this.overlay = undefined;
 | 
			
		||||
            this.dialogVisible = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Stop showing whatever overlay is currently active
 | 
			
		||||
        // (e.g. because the user hit cancel)
 | 
			
		||||
        DialogService.prototype.dismiss = function () {
 | 
			
		||||
            var overlay = this.overlay;
 | 
			
		||||
            if (overlay) {
 | 
			
		||||
                overlay.dismiss();
 | 
			
		||||
            }
 | 
			
		||||
            this.dialogVisible = false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        DialogService.prototype.getDialogResponse = function (key, model, resultGetter) {
 | 
			
		||||
            // We will return this result as a promise, because user
 | 
			
		||||
            // input is asynchronous.
 | 
			
		||||
            var deferred = this.$q.defer(),
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            // Confirm function; this will be passed in to the
 | 
			
		||||
            // overlay-dialog template and associated with a
 | 
			
		||||
            // OK button click
 | 
			
		||||
            function confirm(value) {
 | 
			
		||||
                // Pass along the result
 | 
			
		||||
                deferred.resolve(resultGetter ? resultGetter() : value);
 | 
			
		||||
 | 
			
		||||
                // Stop showing the dialog
 | 
			
		||||
                self.dismiss();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Cancel function; this will be passed in to the
 | 
			
		||||
            // overlay-dialog template and associated with a
 | 
			
		||||
            // Cancel or X button click
 | 
			
		||||
            function cancel() {
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
                self.dismiss();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Add confirm/cancel callbacks
 | 
			
		||||
            model.confirm = confirm;
 | 
			
		||||
            model.cancel = cancel;
 | 
			
		||||
 | 
			
		||||
            if (this.dialogVisible) {
 | 
			
		||||
                // Only one dialog should be shown at a time.
 | 
			
		||||
                // The application design should be such that
 | 
			
		||||
                // we never even try to do this.
 | 
			
		||||
                this.$log.warn([
 | 
			
		||||
                    "Dialog already showing; ",
 | 
			
		||||
                    "unable to show ",
 | 
			
		||||
                    model.name
 | 
			
		||||
                ].join(""));
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            } else {
 | 
			
		||||
                // Add the overlay using the OverlayService, which
 | 
			
		||||
                // will handle actual insertion into the DOM
 | 
			
		||||
                this.overlay = this.overlayService.createOverlay(
 | 
			
		||||
                    key,
 | 
			
		||||
                    model
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                // Track that a dialog is already visible, to
 | 
			
		||||
                // avoid spawning multiple dialogs at once.
 | 
			
		||||
                this.dialogVisible = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return deferred.promise;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Request user input via a window-modal dialog.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {FormModel} formModel a description of the form
 | 
			
		||||
         *        to be shown (see platform/forms)
 | 
			
		||||
         * @param {object} value the initial state of the form
 | 
			
		||||
         * @returns {Promise} a promise for the form value that the
 | 
			
		||||
         *          user has supplied; this may be rejected if
 | 
			
		||||
         *          user input cannot be obtained (for instance,
 | 
			
		||||
         *          because the user cancelled the dialog)
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.getUserInput = function (formModel, value) {
 | 
			
		||||
            var overlayModel = {
 | 
			
		||||
                title: formModel.name,
 | 
			
		||||
                message: formModel.message,
 | 
			
		||||
                structure: formModel,
 | 
			
		||||
                value: value
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Provide result from the model
 | 
			
		||||
            function resultGetter() {
 | 
			
		||||
                return overlayModel.value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Show the overlay-dialog
 | 
			
		||||
            return this.getDialogResponse(
 | 
			
		||||
                "overlay-dialog",
 | 
			
		||||
                overlayModel,
 | 
			
		||||
                resultGetter
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Request that the user chooses from a set of options,
 | 
			
		||||
         * which will be shown as buttons.
 | 
			
		||||
         *
 | 
			
		||||
         * @param dialogModel a description of the dialog to show
 | 
			
		||||
         * @return {Promise} a promise for the user's choice
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.getUserChoice = function (dialogModel) {
 | 
			
		||||
            // Show the overlay-options dialog
 | 
			
		||||
            return this.getDialogResponse(
 | 
			
		||||
                "overlay-options",
 | 
			
		||||
                { dialog: dialogModel }
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return DialogService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -43,57 +43,63 @@ define(
 | 
			
		||||
         * particularly where a multiple-overlay effect is not specifically
 | 
			
		||||
         * desired).
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/dialog
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function OverlayService($document, $compile, $rootScope) {
 | 
			
		||||
            function createOverlay(key, overlayModel) {
 | 
			
		||||
                // Create a new scope for this overlay
 | 
			
		||||
                var scope = $rootScope.$new(),
 | 
			
		||||
                    element;
 | 
			
		||||
            this.$compile = $compile;
 | 
			
		||||
 | 
			
		||||
                // Stop showing the overlay; additionally, release the scope
 | 
			
		||||
                // that it uses.
 | 
			
		||||
                function dismiss() {
 | 
			
		||||
                    scope.$destroy();
 | 
			
		||||
                    element.remove();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // If no model is supplied, just fill in a default "cancel"
 | 
			
		||||
                overlayModel = overlayModel || { cancel: dismiss };
 | 
			
		||||
 | 
			
		||||
                // Populate the scope; will be passed directly to the template
 | 
			
		||||
                scope.overlay = overlayModel;
 | 
			
		||||
                scope.key = key;
 | 
			
		||||
 | 
			
		||||
                // Create the overlay element and add it to the document's body
 | 
			
		||||
                element = $compile(TEMPLATE)(scope);
 | 
			
		||||
                $document.find('body').prepend(element);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                return {
 | 
			
		||||
                    dismiss: dismiss
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Add a new overlay to the document. This will be
 | 
			
		||||
                 * prepended to the document body; the overlay's
 | 
			
		||||
                 * template (as pointed to by the `key` argument) is
 | 
			
		||||
                 * responsible for having a useful z-order, and for
 | 
			
		||||
                 * blocking user interactions if appropriate.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {string} key the symbolic key which identifies
 | 
			
		||||
                 *        the template of the overlay to be shown
 | 
			
		||||
                 * @param {object} overlayModel the model to pass to the
 | 
			
		||||
                 *        included overlay template (this will be passed
 | 
			
		||||
                 *        in via ng-model)
 | 
			
		||||
                 */
 | 
			
		||||
                createOverlay: createOverlay
 | 
			
		||||
            // Don't include $document and $rootScope directly;
 | 
			
		||||
            // avoids https://docs.angularjs.org/error/ng/cpws
 | 
			
		||||
            this.findBody = function () {
 | 
			
		||||
                return $document.find('body');
 | 
			
		||||
            };
 | 
			
		||||
            this.newScope = function () {
 | 
			
		||||
                return $rootScope.$new();
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Add a new overlay to the document. This will be
 | 
			
		||||
         * prepended to the document body; the overlay's
 | 
			
		||||
         * template (as pointed to by the `key` argument) is
 | 
			
		||||
         * responsible for having a useful z-order, and for
 | 
			
		||||
         * blocking user interactions if appropriate.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {string} key the symbolic key which identifies
 | 
			
		||||
         *        the template of the overlay to be shown
 | 
			
		||||
         * @param {object} overlayModel the model to pass to the
 | 
			
		||||
         *        included overlay template (this will be passed
 | 
			
		||||
         *        in via ng-model)
 | 
			
		||||
         */
 | 
			
		||||
        OverlayService.prototype.createOverlay = function (key, overlayModel) {
 | 
			
		||||
            // Create a new scope for this overlay
 | 
			
		||||
            var scope = this.newScope(),
 | 
			
		||||
                element;
 | 
			
		||||
 | 
			
		||||
            // Stop showing the overlay; additionally, release the scope
 | 
			
		||||
            // that it uses.
 | 
			
		||||
            function dismiss() {
 | 
			
		||||
                scope.$destroy();
 | 
			
		||||
                element.remove();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If no model is supplied, just fill in a default "cancel"
 | 
			
		||||
            overlayModel = overlayModel || { cancel: dismiss };
 | 
			
		||||
 | 
			
		||||
            // Populate the scope; will be passed directly to the template
 | 
			
		||||
            scope.overlay = overlayModel;
 | 
			
		||||
            scope.key = key;
 | 
			
		||||
 | 
			
		||||
            // Create the overlay element and add it to the document's body
 | 
			
		||||
            element = this.$compile(TEMPLATE)(scope);
 | 
			
		||||
            this.findBody().prepend(element);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                dismiss: dismiss
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return OverlayService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@
 | 
			
		||||
                "glyph": "Z",
 | 
			
		||||
                "name": "Remove",
 | 
			
		||||
                "description": "Remove this object from its containing object.",
 | 
			
		||||
                "depends": [ "$q" ]
 | 
			
		||||
                "depends": [ "$q", "navigationService" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "save",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
-->
 | 
			
		||||
<span ng-controller="EditActionController">
 | 
			
		||||
    <span ng-repeat="currentAction in editActions">
 | 
			
		||||
        <a class='btn'
 | 
			
		||||
        <a class='s-btn'
 | 
			
		||||
           ng-click="currentAction.perform()"
 | 
			
		||||
           ng-class="{ major: $index === 0, subtle: $index !== 0 }">
 | 
			
		||||
            {{currentAction.getMetadata().name}}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,12 +30,11 @@
 | 
			
		||||
                         structure="toolbar.structure"
 | 
			
		||||
                         ng-model="toolbar.state">
 | 
			
		||||
            </mct-toolbar>
 | 
			
		||||
            <div class='holder abs object-holder work-area'>
 | 
			
		||||
                <mct-representation key="representation.selected.key"
 | 
			
		||||
                                    toolbar="toolbar"
 | 
			
		||||
                                    mct-object="representation.selected.key && domainObject">
 | 
			
		||||
                </mct-representation>
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-representation key="representation.selected.key"
 | 
			
		||||
                                toolbar="toolbar"
 | 
			
		||||
                                mct-object="representation.selected.key && domainObject"
 | 
			
		||||
                                class="holder abs object-holder work-area">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
        </div>
 | 
			
		||||
        <mct-splitter></mct-splitter>
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,26 @@ define(
 | 
			
		||||
         * The "Cancel" action; the action triggered by clicking Cancel from
 | 
			
		||||
         * Edit Mode. Exits the editing user interface and invokes object
 | 
			
		||||
         * capabilities to persist the changes that have been made.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function CancelAction($location, urlService, context) {
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
            this.$location = $location;
 | 
			
		||||
            this.urlService = urlService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Cancel editing.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {Promise} a promise that will be fulfilled when
 | 
			
		||||
         *          cancellation has completed
 | 
			
		||||
         */
 | 
			
		||||
        CancelAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                $location = this.$location,
 | 
			
		||||
                urlService = this.urlService;
 | 
			
		||||
 | 
			
		||||
            // Look up the object's "editor.completion" capability;
 | 
			
		||||
            // this is introduced by EditableDomainObject which is
 | 
			
		||||
@@ -56,25 +73,15 @@ define(
 | 
			
		||||
                )));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Cancel editing.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @returns {Promise} a promise that will be fulfilled when
 | 
			
		||||
                 *          cancellation has completed
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    return doCancel(getEditorCapability())
 | 
			
		||||
                        .then(returnToBrowse);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return doCancel(getEditorCapability())
 | 
			
		||||
                .then(returnToBrowse);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 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
 | 
			
		||||
         * @returns {boolean} true if applicable
 | 
			
		||||
         */
 | 
			
		||||
        CancelAction.appliesTo = function (context) {
 | 
			
		||||
            var domainObject = (context || {}).domainObject;
 | 
			
		||||
@@ -84,4 +91,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return CancelAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,9 @@ define(
 | 
			
		||||
         * mode (typically triggered by the Edit button.) This will
 | 
			
		||||
         * show the user interface for editing (by way of a change in
 | 
			
		||||
         * route)
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function EditAction($location, navigationService, $log, context) {
 | 
			
		||||
            var domainObject = (context || {}).domainObject;
 | 
			
		||||
@@ -60,17 +62,19 @@ define(
 | 
			
		||||
                return NULL_ACTION;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Enter edit mode.
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    navigationService.setNavigation(domainObject);
 | 
			
		||||
                    $location.path("/edit");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.$location = $location;
 | 
			
		||||
            this.navigationService = navigationService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Enter edit mode.
 | 
			
		||||
         */
 | 
			
		||||
        EditAction.prototype.perform = function () {
 | 
			
		||||
            this.navigationService.setNavigation(this.domainObject);
 | 
			
		||||
            this.$location.path("/edit");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check for applicability; verify that a domain object is present
 | 
			
		||||
         * for this action to be performed upon.
 | 
			
		||||
@@ -87,4 +91,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return EditAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,42 +29,36 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Add one domain object to another's composition.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function LinkAction(context) {
 | 
			
		||||
            var domainObject = (context || {}).domainObject,
 | 
			
		||||
                selectedObject = (context || {}).selectedObject,
 | 
			
		||||
                selectedId = selectedObject && selectedObject.getId();
 | 
			
		||||
            this.domainObject = (context || {}).domainObject;
 | 
			
		||||
            this.selectedObject = (context || {}).selectedObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Add this domain object's identifier
 | 
			
		||||
            function addId(model) {
 | 
			
		||||
                if (Array.isArray(model.composition) &&
 | 
			
		||||
                        model.composition.indexOf(selectedId) < 0) {
 | 
			
		||||
                    model.composition.push(selectedId);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        LinkAction.prototype.perform = function () {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Persist changes to the domain object
 | 
			
		||||
            function doPersist() {
 | 
			
		||||
                var persistence = domainObject.getCapability('persistence');
 | 
			
		||||
                var persistence =
 | 
			
		||||
                    self.domainObject.getCapability('persistence');
 | 
			
		||||
                return persistence.persist();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Link these objects
 | 
			
		||||
            function doLink() {
 | 
			
		||||
                return domainObject.useCapability("mutation", addId)
 | 
			
		||||
                    .then(doPersist);
 | 
			
		||||
                var composition = self.domainObject &&
 | 
			
		||||
                        self.domainObject.getCapability('composition');
 | 
			
		||||
                return composition && composition.add(self.selectedObject)
 | 
			
		||||
                        .then(doPersist);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Perform this action.
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    return selectedId && doLink();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return this.selectedObject && doLink();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return LinkAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -32,58 +32,58 @@ define(
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Construct an action which will allow an object's metadata to be
 | 
			
		||||
         * edited.
 | 
			
		||||
         * Implements the "Edit Properties" action, which prompts the user
 | 
			
		||||
         * to modify a domain object's properties.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DialogService} dialogService a service which will show the dialog
 | 
			
		||||
         * @param {DomainObject} object the object to be edited
 | 
			
		||||
         * @param {ActionContext} context the context in which this action is performed
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function PropertiesAction(dialogService, context) {
 | 
			
		||||
            var object = context.domainObject;
 | 
			
		||||
            this.domainObject = (context || {}).domainObject;
 | 
			
		||||
            this.dialogService = dialogService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        PropertiesAction.prototype.perform = function () {
 | 
			
		||||
            var type = this.domainObject.getCapability('type'),
 | 
			
		||||
                domainObject = this.domainObject,
 | 
			
		||||
                dialogService = this.dialogService;
 | 
			
		||||
 | 
			
		||||
            // Persist modifications to this domain object
 | 
			
		||||
            function doPersist() {
 | 
			
		||||
                var persistence = object.getCapability('persistence');
 | 
			
		||||
                var persistence = domainObject.getCapability('persistence');
 | 
			
		||||
                return persistence && persistence.persist();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update the domain object model based on user input
 | 
			
		||||
            function updateModel(userInput, dialog) {
 | 
			
		||||
                return object.useCapability('mutation', function (model) {
 | 
			
		||||
                return domainObject.useCapability('mutation', function (model) {
 | 
			
		||||
                    dialog.updateModel(model, userInput);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function showDialog(type) {
 | 
			
		||||
                // Create a dialog object to generate the form structure, etc.
 | 
			
		||||
                var dialog = new PropertiesDialog(type, object.getModel());
 | 
			
		||||
                var dialog =
 | 
			
		||||
                    new PropertiesDialog(type, domainObject.getModel());
 | 
			
		||||
 | 
			
		||||
                // Show the dialog
 | 
			
		||||
                return dialogService.getUserInput(
 | 
			
		||||
                    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);
 | 
			
		||||
                    }).then(function (result) {
 | 
			
		||||
                        return result && doPersist();
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Perform this action.
 | 
			
		||||
                 * @return {Promise} a promise which will be
 | 
			
		||||
                 *         fulfilled when the action has completed.
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    var type = object.getCapability('type');
 | 
			
		||||
                    return type && showDialog(type);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return type && showDialog(type);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Filter this action for applicability against a given context.
 | 
			
		||||
@@ -106,3 +106,4 @@ define(
 | 
			
		||||
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,6 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Defines the PropertiesDialog, used by the PropertiesAction to
 | 
			
		||||
 * populate the form shown in dialog based on the created type.
 | 
			
		||||
 *
 | 
			
		||||
 * @module common/actions/properties-dialog
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
        'use strict';
 | 
			
		||||
@@ -37,58 +31,63 @@ define(
 | 
			
		||||
         * @param {TypeImpl} type the type of domain object for which properties
 | 
			
		||||
         *        will be specified
 | 
			
		||||
         * @param {DomainObject} the object for which properties will be set
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof module:common/actions/properties-dialog
 | 
			
		||||
         */
 | 
			
		||||
        function PropertiesDialog(type, model) {
 | 
			
		||||
            var properties = type.getProperties();
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get sections provided by this dialog.
 | 
			
		||||
                 * @return {FormStructure} the structure of this form
 | 
			
		||||
                 */
 | 
			
		||||
                getFormStructure: function () {
 | 
			
		||||
                    return {
 | 
			
		||||
                        name: "Edit " + model.name,
 | 
			
		||||
                        sections: [{
 | 
			
		||||
                            name: "Properties",
 | 
			
		||||
                            rows: properties.map(function (property, index) {
 | 
			
		||||
                                // Property definition is same as form row definition
 | 
			
		||||
                                var row = Object.create(property.getDefinition());
 | 
			
		||||
                                row.key = index;
 | 
			
		||||
                                return row;
 | 
			
		||||
                            })
 | 
			
		||||
                        }]
 | 
			
		||||
                    };
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the initial state of the form shown by this dialog
 | 
			
		||||
                 * (based on the object model)
 | 
			
		||||
                 * @returns {object} initial state of the form
 | 
			
		||||
                 */
 | 
			
		||||
                getInitialFormValue: function () {
 | 
			
		||||
                    // Start with initial values for properties
 | 
			
		||||
                    // Note that index needs to correlate to row.key
 | 
			
		||||
                    // from getFormStructure
 | 
			
		||||
                    return properties.map(function (property) {
 | 
			
		||||
                        return property.getValue(model);
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Update a domain object model based on the value of a form.
 | 
			
		||||
                 */
 | 
			
		||||
                updateModel: function (model, formValue) {
 | 
			
		||||
                    // Update all properties
 | 
			
		||||
                    properties.forEach(function (property, index) {
 | 
			
		||||
                        property.setValue(model, formValue[index]);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            this.type = type;
 | 
			
		||||
            this.model = model;
 | 
			
		||||
            this.properties = type.getProperties();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get sections provided by this dialog.
 | 
			
		||||
         * @return {FormStructure} the structure of this form
 | 
			
		||||
         */
 | 
			
		||||
        PropertiesDialog.prototype.getFormStructure = function () {
 | 
			
		||||
            return {
 | 
			
		||||
                name: "Edit " + this.model.name,
 | 
			
		||||
                sections: [{
 | 
			
		||||
                    name: "Properties",
 | 
			
		||||
                    rows: this.properties.map(function (property, index) {
 | 
			
		||||
                        // Property definition is same as form row definition
 | 
			
		||||
                        var row = Object.create(property.getDefinition());
 | 
			
		||||
                        row.key = index;
 | 
			
		||||
                        return row;
 | 
			
		||||
                    }).filter(function (row) {
 | 
			
		||||
                        // Only show properties which are editable
 | 
			
		||||
                        return row.control;
 | 
			
		||||
                    })
 | 
			
		||||
                }]
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the initial state of the form shown by this dialog
 | 
			
		||||
         * (based on the object model)
 | 
			
		||||
         * @returns {object} initial state of the form
 | 
			
		||||
         */
 | 
			
		||||
        PropertiesDialog.prototype.getInitialFormValue = function () {
 | 
			
		||||
            var model = this.model;
 | 
			
		||||
 | 
			
		||||
            // Start with initial values for properties
 | 
			
		||||
            // Note that index needs to correlate to row.key
 | 
			
		||||
            // from getFormStructure
 | 
			
		||||
            return this.properties.map(function (property) {
 | 
			
		||||
                return property.getValue(model);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Update a domain object model based on the value of a form.
 | 
			
		||||
         */
 | 
			
		||||
        PropertiesDialog.prototype.updateModel = function (model, formValue) {
 | 
			
		||||
            // Update all properties
 | 
			
		||||
            this.properties.forEach(function (property, index) {
 | 
			
		||||
                property.setValue(model, formValue[index]);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return PropertiesDialog;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,22 +37,35 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DomainObject} object the object to be removed
 | 
			
		||||
         * @param {ActionContext} context the context in which this action is performed
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof module:editor/actions/remove-action
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         */
 | 
			
		||||
        function RemoveAction($q, context) {
 | 
			
		||||
            var object = (context || {}).domainObject;
 | 
			
		||||
        function RemoveAction($q, navigationService, context) {
 | 
			
		||||
            this.domainObject = (context || {}).domainObject;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.navigationService = navigationService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
        /**
 | 
			
		||||
         * Perform this action.
 | 
			
		||||
         * @return {Promise} a promise which will be
 | 
			
		||||
         *         fulfilled when the action has completed.
 | 
			
		||||
         */
 | 
			
		||||
        RemoveAction.prototype.perform = function () {
 | 
			
		||||
            var $q = this.$q,
 | 
			
		||||
                navigationService = this.navigationService,
 | 
			
		||||
                domainObject = this.domainObject;
 | 
			
		||||
            /*
 | 
			
		||||
             * Check whether an object ID matches the ID of the object being
 | 
			
		||||
             * removed (used to filter a parent's composition to handle the
 | 
			
		||||
             * removal.)
 | 
			
		||||
             */
 | 
			
		||||
            function isNotObject(otherObjectId) {
 | 
			
		||||
                return otherObjectId !== object.getId();
 | 
			
		||||
                return otherObjectId !== domainObject.getId();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
            /*
 | 
			
		||||
             * Mutate a parent object such that it no longer contains the object
 | 
			
		||||
             * which is being removed.
 | 
			
		||||
             */
 | 
			
		||||
@@ -60,7 +73,7 @@ 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.)
 | 
			
		||||
             */
 | 
			
		||||
@@ -69,33 +82,55 @@ define(
 | 
			
		||||
                return persistence && persistence.persist();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Remove the object from its parent, as identified by its context
 | 
			
		||||
             * capability.
 | 
			
		||||
             * @param {ContextCapability} contextCapability the "context" capability
 | 
			
		||||
             *        of the domain object being removed.
 | 
			
		||||
            /*
 | 
			
		||||
             * Checks current object and ascendants of current
 | 
			
		||||
             * object with object being removed, if the current
 | 
			
		||||
             * object or any in the current object's path is being removed,
 | 
			
		||||
             * navigate back to parent of removed object.
 | 
			
		||||
             */
 | 
			
		||||
            function removeFromContext(contextCapability) {
 | 
			
		||||
                var parent = contextCapability.getParent();
 | 
			
		||||
                $q.when(
 | 
			
		||||
            function checkObjectNavigation(object, parentObject) {
 | 
			
		||||
                // Traverse object starts at current location
 | 
			
		||||
                var traverseObject = (navigationService).getNavigation(),
 | 
			
		||||
                    context;
 | 
			
		||||
 | 
			
		||||
                // Stop when object is not defined (above ROOT)
 | 
			
		||||
                while (traverseObject) {
 | 
			
		||||
                    // If object currently traversed to is object being removed
 | 
			
		||||
                    // navigate to parent of current object and then exit loop
 | 
			
		||||
                    if (traverseObject.getId() === object.getId()) {
 | 
			
		||||
                        navigationService.setNavigation(parentObject);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    // Traverses to parent of current object, moving
 | 
			
		||||
                    // up the ascendant path
 | 
			
		||||
                    context = traverseObject.getCapability('context');
 | 
			
		||||
                    traverseObject = context && context.getParent();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * Remove the object from its parent, as identified by its context
 | 
			
		||||
             * capability. Based on object's location and selected object's location
 | 
			
		||||
             * user may be navigated to existing parent object
 | 
			
		||||
             */
 | 
			
		||||
            function removeFromContext(object) {
 | 
			
		||||
                var contextCapability = object.getCapability('context'),
 | 
			
		||||
                    parent = contextCapability.getParent();
 | 
			
		||||
 | 
			
		||||
                // If currently within path of removed object(s),
 | 
			
		||||
                // navigates to existing object up tree
 | 
			
		||||
                checkObjectNavigation(object, parent);
 | 
			
		||||
 | 
			
		||||
                return $q.when(
 | 
			
		||||
                    parent.useCapability('mutation', doMutate)
 | 
			
		||||
                ).then(function () {
 | 
			
		||||
                    return doPersist(parent);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Perform this action.
 | 
			
		||||
                 * @return {module:core/promises.Promise} a promise which will be
 | 
			
		||||
                 *         fulfilled when the action has completed.
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    return $q.when(object.getCapability('context'))
 | 
			
		||||
                        .then(removeFromContext);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return $q.when(domainObject)
 | 
			
		||||
                .then(removeFromContext);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Object needs to have a parent for Remove to be applicable
 | 
			
		||||
        RemoveAction.appliesTo = function (context) {
 | 
			
		||||
@@ -113,4 +148,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return RemoveAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,9 +30,27 @@ define(
 | 
			
		||||
         * The "Save" action; the action triggered by clicking Save from
 | 
			
		||||
         * Edit Mode. Exits the editing user interface and invokes object
 | 
			
		||||
         * capabilities to persist the changes that have been made.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         */
 | 
			
		||||
        function SaveAction($location, urlService, context) {
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
            this.domainObject = (context || {}).domainObject;
 | 
			
		||||
            this.$location = $location;
 | 
			
		||||
            this.urlService = urlService;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Save changes and conclude editing.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {Promise} a promise that will be fulfilled when
 | 
			
		||||
         *          cancellation has completed
 | 
			
		||||
         * @memberof platform/commonUI/edit.SaveAction#
 | 
			
		||||
         */
 | 
			
		||||
        SaveAction.prototype.perform = function () {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                $location = this.$location,
 | 
			
		||||
                urlService = this.urlService;
 | 
			
		||||
 | 
			
		||||
            // Invoke any save behavior introduced by the editor capability;
 | 
			
		||||
            // this is introduced by EditableDomainObject which is
 | 
			
		||||
@@ -51,18 +69,8 @@ define(
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Save changes and conclude editing.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @returns {Promise} a promise that will be fulfilled when
 | 
			
		||||
                 *          cancellation has completed
 | 
			
		||||
                 */
 | 
			
		||||
                perform: function () {
 | 
			
		||||
                    return doSave().then(returnToBrowse);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            return doSave().then(returnToBrowse);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if this action is applicable in a given context.
 | 
			
		||||
@@ -78,4 +86,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return SaveAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,9 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {CompositionCapability}
 | 
			
		||||
         */
 | 
			
		||||
        return function EditableCompositionCapability(
 | 
			
		||||
            contextCapability,
 | 
			
		||||
@@ -54,4 +57,4 @@ define(
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,9 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {ContextCapability}
 | 
			
		||||
         */
 | 
			
		||||
        return function EditableContextCapability(
 | 
			
		||||
            contextCapability,
 | 
			
		||||
@@ -72,4 +75,4 @@ define(
 | 
			
		||||
            return capability;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,8 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         */
 | 
			
		||||
        return function EditableLookupCapability(
 | 
			
		||||
            contextCapability,
 | 
			
		||||
@@ -76,7 +78,7 @@ define(
 | 
			
		||||
            // Wrap a returned value (see above); if it's a promise, wrap
 | 
			
		||||
            // the resolved value.
 | 
			
		||||
            function wrapResult(result) {
 | 
			
		||||
                return result.then ? // promise-like
 | 
			
		||||
                return (result && result.then) ? // promise-like
 | 
			
		||||
                        result.then(makeEditable) :
 | 
			
		||||
                        makeEditable(result);
 | 
			
		||||
            }
 | 
			
		||||
@@ -105,8 +107,10 @@ define(
 | 
			
		||||
 | 
			
		||||
            // Wrap a method of this capability
 | 
			
		||||
            function wrapMethod(fn) {
 | 
			
		||||
                capability[fn] =
 | 
			
		||||
                    (idempotent ? oneTimeFunction : wrapFunction)(fn);
 | 
			
		||||
                if (typeof capability[fn] === 'function') {
 | 
			
		||||
                    capability[fn] =
 | 
			
		||||
                        (idempotent ? oneTimeFunction : wrapFunction)(fn);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Wrap all methods; return only editable domain objects.
 | 
			
		||||
@@ -115,4 +119,4 @@ define(
 | 
			
		||||
            return capability;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,9 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {PersistenceCapability}
 | 
			
		||||
         */
 | 
			
		||||
        function EditablePersistenceCapability(
 | 
			
		||||
            persistenceCapability,
 | 
			
		||||
@@ -62,4 +65,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return EditablePersistenceCapability;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,9 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {RelationshipCapability}
 | 
			
		||||
         */
 | 
			
		||||
        return function EditableRelationshipCapability(
 | 
			
		||||
            relationshipCapability,
 | 
			
		||||
@@ -54,4 +57,4 @@ define(
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,27 +39,48 @@ define(
 | 
			
		||||
         * Meant specifically for use by EditableDomainObject and the
 | 
			
		||||
         * associated cache; the constructor signature is particular
 | 
			
		||||
         * to a pattern used there and may contain unused arguments.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         */
 | 
			
		||||
        return function EditorCapability(
 | 
			
		||||
        function EditorCapability(
 | 
			
		||||
            persistenceCapability,
 | 
			
		||||
            editableObject,
 | 
			
		||||
            domainObject,
 | 
			
		||||
            cache
 | 
			
		||||
        ) {
 | 
			
		||||
            this.editableObject = editableObject;
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.cache = cache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Simulate Promise.resolve (or $q.when); the former
 | 
			
		||||
            // causes a delayed reaction from Angular (since it
 | 
			
		||||
            // does not trigger a digest) and the latter is not
 | 
			
		||||
            // readily accessible, since we're a few classes
 | 
			
		||||
            // removed from the layer which gets dependency
 | 
			
		||||
            // injection.
 | 
			
		||||
            function resolvePromise(value) {
 | 
			
		||||
                return (value && value.then) ? value : {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return resolvePromise(callback(value));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        // Simulate Promise.resolve (or $q.when); the former
 | 
			
		||||
        // causes a delayed reaction from Angular (since it
 | 
			
		||||
        // does not trigger a digest) and the latter is not
 | 
			
		||||
        // readily accessible, since we're a few classes
 | 
			
		||||
        // removed from the layer which gets dependency
 | 
			
		||||
        // injection.
 | 
			
		||||
        function resolvePromise(value) {
 | 
			
		||||
            return (value && value.then) ? value : {
 | 
			
		||||
                then: function (callback) {
 | 
			
		||||
                    return resolvePromise(callback(value));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Save any changes that have been made to this domain object
 | 
			
		||||
         * (as well as to others that might have been retrieved and
 | 
			
		||||
         * modified during the editing session)
 | 
			
		||||
         * @param {boolean} nonrecursive if true, save only this
 | 
			
		||||
         *        object (and not other objects with associated changes)
 | 
			
		||||
         * @returns {Promise} a promise that will be fulfilled after
 | 
			
		||||
         *          persistence has completed.
 | 
			
		||||
         * @memberof platform/commonUI/edit.EditorCapability#
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.save = function (nonrecursive) {
 | 
			
		||||
            var domainObject = this.domainObject,
 | 
			
		||||
                editableObject = this.editableObject,
 | 
			
		||||
                cache = this.cache;
 | 
			
		||||
 | 
			
		||||
            // Update the underlying, "real" domain object's model
 | 
			
		||||
            // with changes made to the copy used for editing.
 | 
			
		||||
@@ -74,39 +95,32 @@ define(
 | 
			
		||||
                return domainObject.getCapability('persistence').persist();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Save any changes that have been made to this domain object
 | 
			
		||||
                 * (as well as to others that might have been retrieved and
 | 
			
		||||
                 * modified during the editing session)
 | 
			
		||||
                 * @param {boolean} nonrecursive if true, save only this
 | 
			
		||||
                 *        object (and not other objects with associated changes)
 | 
			
		||||
                 * @returns {Promise} a promise that will be fulfilled after
 | 
			
		||||
                 *          persistence has completed.
 | 
			
		||||
                 */
 | 
			
		||||
                save: function (nonrecursive) {
 | 
			
		||||
                    return nonrecursive ?
 | 
			
		||||
                            resolvePromise(doMutate()).then(doPersist) :
 | 
			
		||||
                            resolvePromise(cache.saveAll());
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Cancel editing; Discard any changes that have been made to
 | 
			
		||||
                 * this domain object (as well as to others that might have
 | 
			
		||||
                 * been retrieved and modified during the editing session)
 | 
			
		||||
                 * @returns {Promise} a promise that will be fulfilled after
 | 
			
		||||
                 *          cancellation has completed.
 | 
			
		||||
                 */
 | 
			
		||||
                cancel: function () {
 | 
			
		||||
                    return resolvePromise(undefined);
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check if there are any unsaved changes.
 | 
			
		||||
                 * @returns {boolean} true if there are unsaved changes
 | 
			
		||||
                 */
 | 
			
		||||
                dirty: function () {
 | 
			
		||||
                    return cache.dirty();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            return nonrecursive ?
 | 
			
		||||
                resolvePromise(doMutate()).then(doPersist) :
 | 
			
		||||
                resolvePromise(cache.saveAll());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Cancel editing; Discard any changes that have been made to
 | 
			
		||||
         * this domain object (as well as to others that might have
 | 
			
		||||
         * been retrieved and modified during the editing session)
 | 
			
		||||
         * @returns {Promise} a promise that will be fulfilled after
 | 
			
		||||
         *          cancellation has completed.
 | 
			
		||||
         * @memberof platform/commonUI/edit.EditorCapability#
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.cancel = function () {
 | 
			
		||||
            return resolvePromise(undefined);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if there are any unsaved changes.
 | 
			
		||||
         * @returns {boolean} true if there are unsaved changes
 | 
			
		||||
         * @memberof platform/commonUI/edit.EditorCapability#
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.dirty = function () {
 | 
			
		||||
            return this.cache.dirty();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditorCapability;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Controller which supplies action instances for Save/Cancel.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditActionController($scope) {
 | 
			
		||||
@@ -51,4 +52,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return EditActionController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,8 @@
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining EditController. Created by vwoeltje on 11/14/14.
 | 
			
		||||
 * This bundle implements Edit mode.
 | 
			
		||||
 * @namespace platform/commonUI/edit
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["../objects/EditableDomainObject"],
 | 
			
		||||
@@ -33,15 +34,16 @@ define(
 | 
			
		||||
         * Controller which is responsible for populating the scope for
 | 
			
		||||
         * Edit mode; introduces an editable version of the currently
 | 
			
		||||
         * navigated domain object into the scope.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditController($scope, $q, navigationService) {
 | 
			
		||||
            var navigatedObject;
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            function setNavigation(domainObject) {
 | 
			
		||||
                // Wrap the domain object such that all mutation is
 | 
			
		||||
                // confined to edit mode (until Save)
 | 
			
		||||
                navigatedObject =
 | 
			
		||||
                self.navigatedDomainObject =
 | 
			
		||||
                    domainObject && new EditableDomainObject(domainObject, $q);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -50,33 +52,33 @@ define(
 | 
			
		||||
            $scope.$on("$destroy", function () {
 | 
			
		||||
                navigationService.removeListener(setNavigation);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the domain object which is navigated-to.
 | 
			
		||||
                 * @returns {DomainObject} the domain object that is navigated-to
 | 
			
		||||
                 */
 | 
			
		||||
                navigatedObject: function () {
 | 
			
		||||
                    return navigatedObject;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the warning to show if the user attempts to navigate
 | 
			
		||||
                 * away from Edit mode while unsaved changes are present.
 | 
			
		||||
                 * @returns {string} the warning to show, or undefined if
 | 
			
		||||
                 *          there are no unsaved changes
 | 
			
		||||
                 */
 | 
			
		||||
                getUnloadWarning: function () {
 | 
			
		||||
                    var editorCapability = navigatedObject &&
 | 
			
		||||
                            navigatedObject.getCapability("editor"),
 | 
			
		||||
                        hasChanges = editorCapability && editorCapability.dirty();
 | 
			
		||||
 | 
			
		||||
                    return hasChanges ?
 | 
			
		||||
                            "Unsaved changes will be lost if you leave this page." :
 | 
			
		||||
                            undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the domain object which is navigated-to.
 | 
			
		||||
         * @returns {DomainObject} the domain object that is navigated-to
 | 
			
		||||
         */
 | 
			
		||||
        EditController.prototype.navigatedObject = function () {
 | 
			
		||||
            return this.navigatedDomainObject;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the warning to show if the user attempts to navigate
 | 
			
		||||
         * away from Edit mode while unsaved changes are present.
 | 
			
		||||
         * @returns {string} the warning to show, or undefined if
 | 
			
		||||
         *          there are no unsaved changes
 | 
			
		||||
         */
 | 
			
		||||
        EditController.prototype.getUnloadWarning = function () {
 | 
			
		||||
            var navigatedObject = this.navigatedDomainObject,
 | 
			
		||||
                editorCapability = navigatedObject &&
 | 
			
		||||
                    navigatedObject.getCapability("editor"),
 | 
			
		||||
                hasChanges = editorCapability && editorCapability.dirty();
 | 
			
		||||
 | 
			
		||||
            return hasChanges ?
 | 
			
		||||
                "Unsaved changes will be lost if you leave this page." :
 | 
			
		||||
                undefined;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,17 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Supports the Library and Elements panes in Edit mode.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditPanesController($scope) {
 | 
			
		||||
            var root;
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Update root object based on represented object
 | 
			
		||||
            function updateRoot(domainObject) {
 | 
			
		||||
                var context = domainObject &&
 | 
			
		||||
                    domainObject.getCapability('context'),
 | 
			
		||||
                var root = self.rootDomainObject,
 | 
			
		||||
                    context = domainObject &&
 | 
			
		||||
                        domainObject.getCapability('context'),
 | 
			
		||||
                    newRoot = context && context.getTrueRoot(),
 | 
			
		||||
                    oldId = root && root.getId(),
 | 
			
		||||
                    newId = newRoot && newRoot.getId();
 | 
			
		||||
@@ -44,25 +46,22 @@ define(
 | 
			
		||||
                // Only update if this has actually changed,
 | 
			
		||||
                // to avoid excessive refreshing.
 | 
			
		||||
                if (oldId !== newId) {
 | 
			
		||||
                    root = newRoot;
 | 
			
		||||
                    self.rootDomainObject = newRoot;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update root when represented object changes
 | 
			
		||||
            $scope.$watch('domainObject', updateRoot);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the root-level domain object, as reported by the
 | 
			
		||||
                 * represented domain object.
 | 
			
		||||
                 * @returns {DomainObject} the root object
 | 
			
		||||
                 */
 | 
			
		||||
                getRoot: function () {
 | 
			
		||||
                    return root;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the root-level domain object, as reported by the
 | 
			
		||||
         * represented domain object.
 | 
			
		||||
         * @returns {DomainObject} the root object
 | 
			
		||||
         */
 | 
			
		||||
        EditPanesController.prototype.getRoot = function () {
 | 
			
		||||
            return this.rootDomainObject;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditPanesController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define(
 | 
			
		||||
         * to this attribute will be evaluated during page navigation events
 | 
			
		||||
         * and, if it returns a truthy value, will be used to populate a
 | 
			
		||||
         * prompt to the user to confirm this navigation.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $window the window
 | 
			
		||||
         */
 | 
			
		||||
@@ -102,4 +103,4 @@ define(
 | 
			
		||||
        return MCTBeforeUnload;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,9 @@ define(
 | 
			
		||||
         * which need to behave differently in edit mode,
 | 
			
		||||
         * and provides a "working copy" of the object's
 | 
			
		||||
         * model to allow changes to be easily cancelled.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {DomainObject}
 | 
			
		||||
         */
 | 
			
		||||
        function EditableDomainObject(domainObject, $q) {
 | 
			
		||||
            // The cache will hold all domain objects reached from
 | 
			
		||||
@@ -92,10 +95,10 @@ define(
 | 
			
		||||
                            this,
 | 
			
		||||
                            delegateArguments
 | 
			
		||||
                        ),
 | 
			
		||||
                        factory = capabilityFactories[name];
 | 
			
		||||
                        Factory = capabilityFactories[name];
 | 
			
		||||
 | 
			
		||||
                    return (factory && capability) ?
 | 
			
		||||
                            factory(capability, editableObject, domainObject, cache) :
 | 
			
		||||
                    return (Factory && capability) ?
 | 
			
		||||
                            new Factory(capability, editableObject, domainObject, cache) :
 | 
			
		||||
                            capability;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
@@ -109,4 +112,4 @@ define(
 | 
			
		||||
 | 
			
		||||
        return EditableDomainObject;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
/*
 | 
			
		||||
 * An editable domain object cache stores domain objects that have been
 | 
			
		||||
 * made editable, in a group that can be saved all-at-once. This supports
 | 
			
		||||
 * Edit mode, which is launched for a specific object but may contain
 | 
			
		||||
@@ -32,8 +32,6 @@
 | 
			
		||||
 * to ensure that changes made while in edit mode do not propagate up
 | 
			
		||||
 * to the objects used in browse mode (or to persistence) until the user
 | 
			
		||||
 * initiates a Save.
 | 
			
		||||
 *
 | 
			
		||||
 * @module editor/object/editable-domain-object-cache
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["./EditableModelCache"],
 | 
			
		||||
@@ -46,107 +44,118 @@ define(
 | 
			
		||||
         * of objects retrieved via composition or context capabilities as
 | 
			
		||||
         * editable domain objects.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {Constructor<EditableDomainObject>} EditableDomainObject a
 | 
			
		||||
         * @param {Constructor<DomainObject>} EditableDomainObject a
 | 
			
		||||
         *        constructor function which takes a regular domain object as
 | 
			
		||||
         *        an argument, and returns an editable domain object as its
 | 
			
		||||
         *        result.
 | 
			
		||||
         * @param $q Angular's $q, for promise handling
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof module:editor/object/editable-domain-object-cache
 | 
			
		||||
         */
 | 
			
		||||
        function EditableDomainObjectCache(EditableDomainObject, $q) {
 | 
			
		||||
            var cache = new EditableModelCache(),
 | 
			
		||||
                dirty = {},
 | 
			
		||||
                root;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Wrap this domain object in an editable form, or pull such
 | 
			
		||||
                 * an object from the cache if one already exists.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {DomainObject} domainObject the regular domain object
 | 
			
		||||
                 * @returns {DomainObject} the domain object in an editable form
 | 
			
		||||
                 */
 | 
			
		||||
                getEditableObject: function (domainObject) {
 | 
			
		||||
                    var type = domainObject.getCapability('type');
 | 
			
		||||
 | 
			
		||||
                    // Track the top-level domain object; this will have
 | 
			
		||||
                    // some special behavior for its context capability.
 | 
			
		||||
                    root = root || domainObject;
 | 
			
		||||
 | 
			
		||||
                    // Avoid double-wrapping (WTD-1017)
 | 
			
		||||
                    if (domainObject.hasCapability('editor')) {
 | 
			
		||||
                        return domainObject;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Don't bother wrapping non-editable objects
 | 
			
		||||
                    if (!type || !type.hasFeature('creation')) {
 | 
			
		||||
                        return domainObject;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Provide an editable form of the object
 | 
			
		||||
                    return new EditableDomainObject(
 | 
			
		||||
                        domainObject,
 | 
			
		||||
                        cache.getCachedModel(domainObject)
 | 
			
		||||
                    );
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check if a domain object is (effectively) the top-level
 | 
			
		||||
                 * object in this editable subgraph.
 | 
			
		||||
                 * @returns {boolean} true if it is the root
 | 
			
		||||
                 */
 | 
			
		||||
                isRoot: function (domainObject) {
 | 
			
		||||
                    return domainObject === root;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Mark an editable domain object (presumably already cached)
 | 
			
		||||
                 * as having received modifications during editing; it should be
 | 
			
		||||
                 * included in the bulk save invoked when editing completes.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {DomainObject} domainObject the domain object
 | 
			
		||||
                 */
 | 
			
		||||
                markDirty: function (domainObject) {
 | 
			
		||||
                    dirty[domainObject.getId()] = domainObject;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Mark an object (presumably already cached) as having had its
 | 
			
		||||
                 * changes saved (and thus no longer needing to be subject to a
 | 
			
		||||
                 * save operation.)
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {DomainObject} domainObject the domain object
 | 
			
		||||
                 */
 | 
			
		||||
                markClean: function (domainObject) {
 | 
			
		||||
                    delete dirty[domainObject.getId()];
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Initiate a save on all objects that have been cached.
 | 
			
		||||
                 */
 | 
			
		||||
                saveAll: function () {
 | 
			
		||||
                    // Get a list of all dirty objects
 | 
			
		||||
                    var objects = Object.keys(dirty).map(function (k) {
 | 
			
		||||
                        return dirty[k];
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    // Clear dirty set, since we're about to save.
 | 
			
		||||
                    dirty = {};
 | 
			
		||||
 | 
			
		||||
                    // Most save logic is handled by the "editor.completion"
 | 
			
		||||
                    // capability, so that is delegated here.
 | 
			
		||||
                    return $q.all(objects.map(function (object) {
 | 
			
		||||
                        // Save; pass a nonrecursive flag to avoid looping
 | 
			
		||||
                        return object.getCapability('editor').save(true);
 | 
			
		||||
                    }));
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check if any objects have been marked dirty in this cache.
 | 
			
		||||
                 * @returns {boolean} true if objects are dirty
 | 
			
		||||
                 */
 | 
			
		||||
                dirty: function () {
 | 
			
		||||
                    return Object.keys(dirty).length > 0;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.cache = new EditableModelCache();
 | 
			
		||||
            this.dirtyObjects = {};
 | 
			
		||||
            this.root = undefined;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.EditableDomainObject = EditableDomainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Wrap this domain object in an editable form, or pull such
 | 
			
		||||
         * an object from the cache if one already exists.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DomainObject} domainObject the regular domain object
 | 
			
		||||
         * @returns {DomainObject} the domain object in an editable form
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.getEditableObject = function (domainObject) {
 | 
			
		||||
            var type = domainObject.getCapability('type'),
 | 
			
		||||
                EditableDomainObject = this.EditableDomainObject;
 | 
			
		||||
 | 
			
		||||
            // Track the top-level domain object; this will have
 | 
			
		||||
            // some special behavior for its context capability.
 | 
			
		||||
            this.root = this.root || domainObject;
 | 
			
		||||
 | 
			
		||||
            // Avoid double-wrapping (WTD-1017)
 | 
			
		||||
            if (domainObject.hasCapability('editor')) {
 | 
			
		||||
                return domainObject;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Don't bother wrapping non-editable objects
 | 
			
		||||
            if (!type || !type.hasFeature('creation')) {
 | 
			
		||||
                return domainObject;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Provide an editable form of the object
 | 
			
		||||
            return new EditableDomainObject(
 | 
			
		||||
                domainObject,
 | 
			
		||||
                this.cache.getCachedModel(domainObject)
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if a domain object is (effectively) the top-level
 | 
			
		||||
         * object in this editable subgraph.
 | 
			
		||||
         * @returns {boolean} true if it is the root
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.isRoot = function (domainObject) {
 | 
			
		||||
            return domainObject === this.root;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Mark an editable domain object (presumably already cached)
 | 
			
		||||
         * as having received modifications during editing; it should be
 | 
			
		||||
         * included in the bulk save invoked when editing completes.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DomainObject} domainObject the domain object
 | 
			
		||||
         * @memberof platform/commonUI/edit.EditableDomainObjectCache#
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.markDirty = function (domainObject) {
 | 
			
		||||
            this.dirtyObjects[domainObject.getId()] = domainObject;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Mark an object (presumably already cached) as having had its
 | 
			
		||||
         * changes saved (and thus no longer needing to be subject to a
 | 
			
		||||
         * save operation.)
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DomainObject} domainObject the domain object
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.markClean = function (domainObject) {
 | 
			
		||||
            delete this.dirtyObjects[domainObject.getId()];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Initiate a save on all objects that have been cached.
 | 
			
		||||
         * @return {Promise} A promise which will resolve when all objects are
 | 
			
		||||
         *         persisted.
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.saveAll = function () {
 | 
			
		||||
            // Get a list of all dirty objects
 | 
			
		||||
            var dirty = this.dirtyObjects,
 | 
			
		||||
                objects = Object.keys(dirty).map(function (k) {
 | 
			
		||||
                    return dirty[k];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            // Clear dirty set, since we're about to save.
 | 
			
		||||
            this.dirtyObjects = {};
 | 
			
		||||
 | 
			
		||||
            // Most save logic is handled by the "editor.completion"
 | 
			
		||||
            // capability, so that is delegated here.
 | 
			
		||||
            return this.$q.all(objects.map(function (object) {
 | 
			
		||||
                // Save; pass a nonrecursive flag to avoid looping
 | 
			
		||||
                return object.getCapability('editor').save(true);
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if any objects have been marked dirty in this cache.
 | 
			
		||||
         * @returns {boolean} true if objects are dirty
 | 
			
		||||
         */
 | 
			
		||||
        EditableDomainObjectCache.prototype.dirty = function () {
 | 
			
		||||
            return Object.keys(this.dirtyObjects).length > 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditableDomainObjectCache;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,33 +31,32 @@ define(
 | 
			
		||||
         * made editable, to support a group that can be saved all-at-once.
 | 
			
		||||
         * This is useful in Edit mode, which is launched for a specific
 | 
			
		||||
         * object but may contain changes across many objects.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditableModelCache() {
 | 
			
		||||
            var cache = {};
 | 
			
		||||
 | 
			
		||||
            // Deep-copy a model. Models are JSONifiable, so this can be
 | 
			
		||||
            // done by stringification then destringification
 | 
			
		||||
            function clone(model) {
 | 
			
		||||
                return JSON.parse(JSON.stringify(model));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get this domain object's model from the cache (or
 | 
			
		||||
                 * place it in the cache if it isn't in the cache yet)
 | 
			
		||||
                 * @returns a clone of the domain object's model
 | 
			
		||||
                 */
 | 
			
		||||
                getCachedModel: function (domainObject) {
 | 
			
		||||
                    var id = domainObject.getId();
 | 
			
		||||
 | 
			
		||||
                    return (cache[id] =
 | 
			
		||||
                        cache[id] || clone(domainObject.getModel()));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.cache = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Deep-copy a model. Models are JSONifiable, so this can be
 | 
			
		||||
        // done by stringification then destringification
 | 
			
		||||
        function clone(model) {
 | 
			
		||||
            return JSON.parse(JSON.stringify(model));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get this domain object's model from the cache (or
 | 
			
		||||
         * place it in the cache if it isn't in the cache yet)
 | 
			
		||||
         * @returns a clone of the domain object's model
 | 
			
		||||
         */
 | 
			
		||||
        EditableModelCache.prototype.getCachedModel = function (domainObject) {
 | 
			
		||||
            var id = domainObject.getId(),
 | 
			
		||||
                cache = this.cache;
 | 
			
		||||
 | 
			
		||||
            return (cache[id] =
 | 
			
		||||
                cache[id] || clone(domainObject.getModel()));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditableModelCache;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,53 +30,47 @@ define(
 | 
			
		||||
         * Policy controlling when the `edit` and/or `properties` actions
 | 
			
		||||
         * can appear as applicable actions of the `view-control` category
 | 
			
		||||
         * (shown as buttons in the top-right of browse mode.)
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Policy.<Action, ActionContext>}
 | 
			
		||||
         */
 | 
			
		||||
        function EditActionPolicy() {
 | 
			
		||||
            // Get a count of views which are not flagged as non-editable.
 | 
			
		||||
            function countEditableViews(context) {
 | 
			
		||||
                var domainObject = (context || {}).domainObject,
 | 
			
		||||
                    views = domainObject && domainObject.useCapability('view'),
 | 
			
		||||
                    count = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                // A view is editable unless explicitly flagged as not
 | 
			
		||||
                (views || []).forEach(function (view) {
 | 
			
		||||
                    count += (view.editable !== false) ? 1 : 0;
 | 
			
		||||
                });
 | 
			
		||||
        // Get a count of views which are not flagged as non-editable.
 | 
			
		||||
        function countEditableViews(context) {
 | 
			
		||||
            var domainObject = (context || {}).domainObject,
 | 
			
		||||
                views = domainObject && domainObject.useCapability('view'),
 | 
			
		||||
                count = 0;
 | 
			
		||||
 | 
			
		||||
                return count;
 | 
			
		||||
            // A view is editable unless explicitly flagged as not
 | 
			
		||||
            (views || []).forEach(function (view) {
 | 
			
		||||
                count += (view.editable !== false) ? 1 : 0;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EditActionPolicy.prototype.allow = function (action, context) {
 | 
			
		||||
            var key = action.getMetadata().key,
 | 
			
		||||
                category = (context || {}).category;
 | 
			
		||||
 | 
			
		||||
            // Only worry about actions in the view-control category
 | 
			
		||||
            if (category === 'view-control') {
 | 
			
		||||
                // Restrict 'edit' to cases where there are editable
 | 
			
		||||
                // views (similarly, restrict 'properties' to when
 | 
			
		||||
                // the converse is true)
 | 
			
		||||
                if (key === 'edit') {
 | 
			
		||||
                    return countEditableViews(context) > 0;
 | 
			
		||||
                } else if (key === 'properties') {
 | 
			
		||||
                    return countEditableViews(context) < 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check whether or not a given action is allowed by this
 | 
			
		||||
                 * policy.
 | 
			
		||||
                 * @param {Action} action the action
 | 
			
		||||
                 * @param context the context
 | 
			
		||||
                 * @returns {boolean} true if not disallowed
 | 
			
		||||
                 */
 | 
			
		||||
                allow: function (action, context) {
 | 
			
		||||
                    var key = action.getMetadata().key,
 | 
			
		||||
                        category = (context || {}).category;
 | 
			
		||||
 | 
			
		||||
                    // Only worry about actions in the view-control category
 | 
			
		||||
                    if (category === 'view-control') {
 | 
			
		||||
                        // Restrict 'edit' to cases where there are editable
 | 
			
		||||
                        // views (similarly, restrict 'properties' to when
 | 
			
		||||
                        // the converse is true)
 | 
			
		||||
                        if (key === 'edit') {
 | 
			
		||||
                            return countEditableViews(context) > 0;
 | 
			
		||||
                        } else if (key === 'properties') {
 | 
			
		||||
                            return countEditableViews(context) < 1;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Like all policies, allow by default.
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
            // Like all policies, allow by default.
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditActionPolicy;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,30 +28,24 @@ define(
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Policy controlling which views should be visible in Edit mode.
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Policy.<View, DomainObject>}
 | 
			
		||||
         */
 | 
			
		||||
        function EditableViewPolicy() {
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check whether or not a given action is allowed by this
 | 
			
		||||
                 * policy.
 | 
			
		||||
                 * @param {Action} action the action
 | 
			
		||||
                 * @param domainObject the domain object which will be viewed
 | 
			
		||||
                 * @returns {boolean} true if not disallowed
 | 
			
		||||
                 */
 | 
			
		||||
                allow: function (view, domainObject) {
 | 
			
		||||
                    // If a view is flagged as non-editable, only allow it
 | 
			
		||||
                    // while we're not in Edit mode.
 | 
			
		||||
                    if ((view || {}).editable === false) {
 | 
			
		||||
                        return !domainObject.hasCapability('editor');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Like all policies, allow by default.
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EditableViewPolicy.prototype.allow = function (view, domainObject) {
 | 
			
		||||
            // If a view is flagged as non-editable, only allow it
 | 
			
		||||
            // while we're not in Edit mode.
 | 
			
		||||
            if ((view || {}).editable === false) {
 | 
			
		||||
                return !domainObject.hasCapability('editor');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Like all policies, allow by default.
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditableViewPolicy;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,14 +41,17 @@ define(
 | 
			
		||||
         * and may be reused for different domain objects and/or
 | 
			
		||||
         * representations resulting from changes there.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @implements {Representer}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditRepresenter($q, $log, scope) {
 | 
			
		||||
            var domainObject,
 | 
			
		||||
                key;
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Mutate and persist a new version of a domain object's model.
 | 
			
		||||
            function doPersist(model) {
 | 
			
		||||
                var domainObject = self.domainObject;
 | 
			
		||||
 | 
			
		||||
                // First, mutate; then, persist.
 | 
			
		||||
                return $q.when(domainObject.useCapability("mutation", function () {
 | 
			
		||||
                    return model;
 | 
			
		||||
@@ -64,7 +67,8 @@ define(
 | 
			
		||||
                // Look up from scope; these will have been populated by
 | 
			
		||||
                // mct-representation.
 | 
			
		||||
                var model = scope.model,
 | 
			
		||||
                    configuration = scope.configuration;
 | 
			
		||||
                    configuration = scope.configuration,
 | 
			
		||||
                    domainObject = self.domainObject;
 | 
			
		||||
 | 
			
		||||
                // Log the commit message
 | 
			
		||||
                $log.debug([
 | 
			
		||||
@@ -78,50 +82,33 @@ define(
 | 
			
		||||
                if (domainObject && domainObject.hasCapability("persistence")) {
 | 
			
		||||
                    // Configurations for specific views are stored by
 | 
			
		||||
                    // key in the "configuration" field of the model.
 | 
			
		||||
                    if (key && configuration) {
 | 
			
		||||
                    if (self.key && configuration) {
 | 
			
		||||
                        model.configuration = model.configuration || {};
 | 
			
		||||
                        model.configuration[key] = configuration;
 | 
			
		||||
                        model.configuration[self.key] = configuration;
 | 
			
		||||
                    }
 | 
			
		||||
                    doPersist(model);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Respond to the destruction of the current representation.
 | 
			
		||||
            function destroy() {
 | 
			
		||||
                // Nothing to clean up
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Handle a specific representation of a specific domain object
 | 
			
		||||
            function represent(representation, representedObject) {
 | 
			
		||||
                // Track the key, to know which view configuration to save to.
 | 
			
		||||
                key = (representation || {}).key;
 | 
			
		||||
                // Track the represented object
 | 
			
		||||
                domainObject = representedObject;
 | 
			
		||||
                // Ensure existing watches are released
 | 
			
		||||
                destroy();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Place the "commit" method in the scope
 | 
			
		||||
            scope.commit = commit;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Set the current representation in use, and the domain
 | 
			
		||||
                 * object being represented.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {RepresentationDefinition} representation the
 | 
			
		||||
                 *        definition of the representation in use
 | 
			
		||||
                 * @param {DomainObject} domainObject the domain object
 | 
			
		||||
                 *        being represented
 | 
			
		||||
                 */
 | 
			
		||||
                represent: represent,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Release any resources associated with this representer.
 | 
			
		||||
                 */
 | 
			
		||||
                destroy: destroy
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handle a specific representation of a specific domain object
 | 
			
		||||
        EditRepresenter.prototype.represent = function represent(representation, representedObject) {
 | 
			
		||||
            // Track the key, to know which view configuration to save to.
 | 
			
		||||
            this.key = (representation || {}).key;
 | 
			
		||||
            // Track the represented object
 | 
			
		||||
            this.domainObject = representedObject;
 | 
			
		||||
            // Ensure existing watches are released
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Respond to the destruction of the current representation.
 | 
			
		||||
        EditRepresenter.prototype.destroy = function destroy() {
 | 
			
		||||
            // Nothing to clean up
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditRepresenter;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -38,125 +38,23 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @param structure toolbar structure, as provided by view definition
 | 
			
		||||
         * @param {Function} commit callback to invoke after changes
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditToolbar(structure, commit) {
 | 
			
		||||
            var toolbarStructure = Object.create(structure || {}),
 | 
			
		||||
                toolbarState,
 | 
			
		||||
                selection,
 | 
			
		||||
                properties = [];
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Generate a new key for an item's property
 | 
			
		||||
            function addKey(property) {
 | 
			
		||||
                properties.push(property);
 | 
			
		||||
                return properties.length - 1; // Return index of property
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update value for this property in all elements of the
 | 
			
		||||
            // selection which have this property.
 | 
			
		||||
            function updateProperties(property, value) {
 | 
			
		||||
                var changed = false;
 | 
			
		||||
 | 
			
		||||
                // Update property in a selected element
 | 
			
		||||
                function updateProperty(selected) {
 | 
			
		||||
                    // Ignore selected elements which don't have this property
 | 
			
		||||
                    if (selected[property] !== undefined) {
 | 
			
		||||
                        // Check if this is a setter, or just assignable
 | 
			
		||||
                        if (typeof selected[property] === 'function') {
 | 
			
		||||
                            changed =
 | 
			
		||||
                                changed || (selected[property]() !== value);
 | 
			
		||||
                            selected[property](value);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            changed =
 | 
			
		||||
                                changed || (selected[property] !== value);
 | 
			
		||||
                            selected[property] = value;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Update property in all selected elements
 | 
			
		||||
                selection.forEach(updateProperty);
 | 
			
		||||
 | 
			
		||||
                // Return whether or not anything changed
 | 
			
		||||
                return changed;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Look up the current value associated with a property
 | 
			
		||||
            // in selection i
 | 
			
		||||
            function lookupState(property, selected) {
 | 
			
		||||
                var value = selected[property];
 | 
			
		||||
                return (typeof value === 'function') ? value() : value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Get initial value for a given property
 | 
			
		||||
            function initializeState(property) {
 | 
			
		||||
                var result;
 | 
			
		||||
                // Look through all selections for this property;
 | 
			
		||||
                // values should all match by the time we perform
 | 
			
		||||
                // this lookup anyway.
 | 
			
		||||
                selection.forEach(function (selected) {
 | 
			
		||||
                    result = (selected[property] !== undefined) ?
 | 
			
		||||
                            lookupState(property, selected) :
 | 
			
		||||
                            result;
 | 
			
		||||
                });
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check if all elements of the selection which have this
 | 
			
		||||
            // property have the same value for this property.
 | 
			
		||||
            function isConsistent(property) {
 | 
			
		||||
                var consistent = true,
 | 
			
		||||
                    observed = false,
 | 
			
		||||
                    state;
 | 
			
		||||
 | 
			
		||||
                // Check if a given element of the selection is consistent
 | 
			
		||||
                // with previously-observed elements for this property.
 | 
			
		||||
                function checkConsistency(selected) {
 | 
			
		||||
                    var next;
 | 
			
		||||
                    // Ignore selections which don't have this property
 | 
			
		||||
                    if (selected[property] !== undefined) {
 | 
			
		||||
                        // Look up state of this element in the selection
 | 
			
		||||
                        next = lookupState(property, selected);
 | 
			
		||||
                        // Detect inconsistency
 | 
			
		||||
                        if (observed) {
 | 
			
		||||
                            consistent = consistent && (next === state);
 | 
			
		||||
                        }
 | 
			
		||||
                        // Track state for next iteration
 | 
			
		||||
                        state = next;
 | 
			
		||||
                        observed = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Iterate through selections
 | 
			
		||||
                selection.forEach(checkConsistency);
 | 
			
		||||
 | 
			
		||||
                return consistent;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Used to filter out items which are applicable (or not)
 | 
			
		||||
            // to the current selection.
 | 
			
		||||
            function isApplicable(item) {
 | 
			
		||||
                var property = (item || {}).property,
 | 
			
		||||
                    method = (item || {}).method,
 | 
			
		||||
                    exclusive = !!(item || {}).exclusive;
 | 
			
		||||
 | 
			
		||||
                // Check if a selected item defines this property
 | 
			
		||||
                function hasProperty(selected) {
 | 
			
		||||
                    return (property && (selected[property] !== undefined)) ||
 | 
			
		||||
                            (method && (typeof selected[method] === 'function'));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return selection.map(hasProperty).reduce(
 | 
			
		||||
                    exclusive ? and : or,
 | 
			
		||||
                    exclusive
 | 
			
		||||
                ) && isConsistent(property);
 | 
			
		||||
                self.properties.push(property);
 | 
			
		||||
                return self.properties.length - 1; // Return index of property
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Invoke all functions in selections with the given name
 | 
			
		||||
            function invoke(method, value) {
 | 
			
		||||
                if (method) {
 | 
			
		||||
                    // Make the change in the selection
 | 
			
		||||
                    selection.forEach(function (selected) {
 | 
			
		||||
                    self.selection.forEach(function (selected) {
 | 
			
		||||
                        if (typeof selected[method] === 'function') {
 | 
			
		||||
                            selected[method](value);
 | 
			
		||||
                        }
 | 
			
		||||
@@ -189,73 +87,172 @@ define(
 | 
			
		||||
                return converted;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.toolbarState = [];
 | 
			
		||||
            this.selection = undefined;
 | 
			
		||||
            this.properties = [];
 | 
			
		||||
            this.toolbarStructure = Object.create(structure || {});
 | 
			
		||||
            this.toolbarStructure.sections =
 | 
			
		||||
                ((structure || {}).sections || []).map(convertSection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if all elements of the selection which have this
 | 
			
		||||
        // property have the same value for this property.
 | 
			
		||||
        EditToolbar.prototype.isConsistent = function (property) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                consistent = true,
 | 
			
		||||
                observed = false,
 | 
			
		||||
                state;
 | 
			
		||||
 | 
			
		||||
            // Check if a given element of the selection is consistent
 | 
			
		||||
            // with previously-observed elements for this property.
 | 
			
		||||
            function checkConsistency(selected) {
 | 
			
		||||
                var next;
 | 
			
		||||
                // Ignore selections which don't have this property
 | 
			
		||||
                if (selected[property] !== undefined) {
 | 
			
		||||
                    // Look up state of this element in the selection
 | 
			
		||||
                    next = self.lookupState(property, selected);
 | 
			
		||||
                    // Detect inconsistency
 | 
			
		||||
                    if (observed) {
 | 
			
		||||
                        consistent = consistent && (next === state);
 | 
			
		||||
                    }
 | 
			
		||||
                    // Track state for next iteration
 | 
			
		||||
                    state = next;
 | 
			
		||||
                    observed = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Iterate through selections
 | 
			
		||||
            self.selection.forEach(checkConsistency);
 | 
			
		||||
 | 
			
		||||
            return consistent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Used to filter out items which are applicable (or not)
 | 
			
		||||
        // to the current selection.
 | 
			
		||||
        EditToolbar.prototype.isApplicable = function (item) {
 | 
			
		||||
            var property = (item || {}).property,
 | 
			
		||||
                method = (item || {}).method,
 | 
			
		||||
                exclusive = !!(item || {}).exclusive;
 | 
			
		||||
 | 
			
		||||
            // Check if a selected item defines this property
 | 
			
		||||
            function hasProperty(selected) {
 | 
			
		||||
                return (property && (selected[property] !== undefined)) ||
 | 
			
		||||
                    (method && (typeof selected[method] === 'function'));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this.selection.map(hasProperty).reduce(
 | 
			
		||||
                    exclusive ? and : or,
 | 
			
		||||
                    exclusive
 | 
			
		||||
                ) && this.isConsistent(property);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Look up the current value associated with a property
 | 
			
		||||
        EditToolbar.prototype.lookupState = function (property, selected) {
 | 
			
		||||
            var value = selected[property];
 | 
			
		||||
            return (typeof value === 'function') ? value() : value;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Set the current selection. Visibility of sections
 | 
			
		||||
         * and items in the toolbar will be updated to match this.
 | 
			
		||||
         * @param {Array} s the new selection
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbar.prototype.setSelection = function (s) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Show/hide controls in this section per applicability
 | 
			
		||||
            function refreshSectionApplicability(section) {
 | 
			
		||||
                var count = 0;
 | 
			
		||||
                // Show/hide each item
 | 
			
		||||
                (section.items || []).forEach(function (item) {
 | 
			
		||||
                    item.hidden = !isApplicable(item);
 | 
			
		||||
                    item.hidden = !self.isApplicable(item);
 | 
			
		||||
                    count += item.hidden ? 0 : 1;
 | 
			
		||||
                });
 | 
			
		||||
                // Hide this section if there are no applicable items
 | 
			
		||||
                section.hidden = !count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Show/hide controls if they are applicable
 | 
			
		||||
            function refreshApplicability() {
 | 
			
		||||
                toolbarStructure.sections.forEach(refreshSectionApplicability);
 | 
			
		||||
            // Get initial value for a given property
 | 
			
		||||
            function initializeState(property) {
 | 
			
		||||
                var result;
 | 
			
		||||
                // Look through all selections for this property;
 | 
			
		||||
                // values should all match by the time we perform
 | 
			
		||||
                // this lookup anyway.
 | 
			
		||||
                self.selection.forEach(function (selected) {
 | 
			
		||||
                    result = (selected[property] !== undefined) ?
 | 
			
		||||
                        self.lookupState(property, selected) :
 | 
			
		||||
                        result;
 | 
			
		||||
                });
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Refresh toolbar state to match selection
 | 
			
		||||
            function refreshState() {
 | 
			
		||||
                toolbarState = properties.map(initializeState);
 | 
			
		||||
            }
 | 
			
		||||
            this.selection = s;
 | 
			
		||||
            this.toolbarStructure.sections.forEach(refreshSectionApplicability);
 | 
			
		||||
            this.toolbarState = this.properties.map(initializeState);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            toolbarStructure.sections =
 | 
			
		||||
                ((structure || {}).sections || []).map(convertSection);
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the structure of the toolbar, as appropriate to
 | 
			
		||||
         * pass to `mct-toolbar`.
 | 
			
		||||
         * @returns the toolbar structure
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbar.prototype.getStructure = function () {
 | 
			
		||||
            return this.toolbarStructure;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            toolbarState = [];
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the current state of the toolbar, as appropriate
 | 
			
		||||
         * to two-way bind to the state handled by `mct-toolbar`.
 | 
			
		||||
         * @returns {Array} state of the toolbar
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbar.prototype.getState = function () {
 | 
			
		||||
            return this.toolbarState;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Set the current selection. Visisbility of sections
 | 
			
		||||
                 * and items in the toolbar will be updated to match this.
 | 
			
		||||
                 * @param {Array} s the new selection
 | 
			
		||||
                 */
 | 
			
		||||
                setSelection: function (s) {
 | 
			
		||||
                    selection = s;
 | 
			
		||||
                    refreshApplicability();
 | 
			
		||||
                    refreshState();
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the structure of the toolbar, as appropriate to
 | 
			
		||||
                 * pass to `mct-toolbar`.
 | 
			
		||||
                 * @returns the toolbar structure
 | 
			
		||||
                 */
 | 
			
		||||
                getStructure: function () {
 | 
			
		||||
                    return toolbarStructure;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the current state of the toolbar, as appropriate
 | 
			
		||||
                 * to two-way bind to the state handled by `mct-toolbar`.
 | 
			
		||||
                 * @returns {Array} state of the toolbar
 | 
			
		||||
                 */
 | 
			
		||||
                getState: function () {
 | 
			
		||||
                    return toolbarState;
 | 
			
		||||
                },
 | 
			
		||||
                /**
 | 
			
		||||
                 * Update state within the current selection.
 | 
			
		||||
                 * @param {number} index the index of the corresponding
 | 
			
		||||
                 *        element in the state array
 | 
			
		||||
                 * @param value the new value to convey to the selection
 | 
			
		||||
                 */
 | 
			
		||||
                updateState: function (index, value) {
 | 
			
		||||
                    return updateProperties(properties[index], value);
 | 
			
		||||
        /**
 | 
			
		||||
         * Update state within the current selection.
 | 
			
		||||
         * @param {number} index the index of the corresponding
 | 
			
		||||
         *        element in the state array
 | 
			
		||||
         * @param value the new value to convey to the selection
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbar.prototype.updateState = function (index, value) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Update value for this property in all elements of the
 | 
			
		||||
            // selection which have this property.
 | 
			
		||||
            function updateProperties(property, value) {
 | 
			
		||||
                var changed = false;
 | 
			
		||||
 | 
			
		||||
                // Update property in a selected element
 | 
			
		||||
                function updateProperty(selected) {
 | 
			
		||||
                    // Ignore selected elements which don't have this property
 | 
			
		||||
                    if (selected[property] !== undefined) {
 | 
			
		||||
                        // Check if this is a setter, or just assignable
 | 
			
		||||
                        if (typeof selected[property] === 'function') {
 | 
			
		||||
                            changed =
 | 
			
		||||
                                changed || (selected[property]() !== value);
 | 
			
		||||
                            selected[property](value);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            changed =
 | 
			
		||||
                                changed || (selected[property] !== value);
 | 
			
		||||
                            selected[property] = value;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                // Update property in all selected elements
 | 
			
		||||
                self.selection.forEach(updateProperty);
 | 
			
		||||
 | 
			
		||||
                // Return whether or not anything changed
 | 
			
		||||
                return changed;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return updateProperties(this.properties[index], value);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditToolbar;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,17 +27,21 @@ define(
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        // No operation
 | 
			
		||||
        function noop() {}
 | 
			
		||||
        var NOOP_REPRESENTER = {
 | 
			
		||||
            represent: function () {},
 | 
			
		||||
            destroy: function () {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The EditToolbarRepresenter populates the toolbar in Edit mode
 | 
			
		||||
         * based on a view's definition.
 | 
			
		||||
         * @param {Scope} scope the Angular scope of the representation
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @implements {Representer}
 | 
			
		||||
         */
 | 
			
		||||
        function EditToolbarRepresenter(scope, element, attrs) {
 | 
			
		||||
            var toolbar,
 | 
			
		||||
                toolbarObject = {};
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            // Mark changes as ready to persist
 | 
			
		||||
            function commit(message) {
 | 
			
		||||
@@ -49,31 +53,33 @@ define(
 | 
			
		||||
            // Handle changes to the current selection
 | 
			
		||||
            function updateSelection(selection) {
 | 
			
		||||
                // Only update if there is a toolbar to update
 | 
			
		||||
                if (toolbar) {
 | 
			
		||||
                if (self.toolbar) {
 | 
			
		||||
                    // Make sure selection is array-like
 | 
			
		||||
                    selection = Array.isArray(selection) ?
 | 
			
		||||
                            selection :
 | 
			
		||||
                            (selection ? [selection] : []);
 | 
			
		||||
 | 
			
		||||
                    // Update the toolbar's selection
 | 
			
		||||
                    toolbar.setSelection(selection);
 | 
			
		||||
                    self.toolbar.setSelection(selection);
 | 
			
		||||
 | 
			
		||||
                    // ...and expose its structure/state
 | 
			
		||||
                    toolbarObject.structure = toolbar.getStructure();
 | 
			
		||||
                    toolbarObject.state = toolbar.getState();
 | 
			
		||||
                    self.toolbarObject.structure =
 | 
			
		||||
                        self.toolbar.getStructure();
 | 
			
		||||
                    self.toolbarObject.state =
 | 
			
		||||
                        self.toolbar.getState();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Get state (to watch it)
 | 
			
		||||
            function getState() {
 | 
			
		||||
                return toolbarObject.state;
 | 
			
		||||
                return self.toolbarObject.state;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update selection models to match changed toolbar state
 | 
			
		||||
            function updateState(state) {
 | 
			
		||||
                // Update underlying state based on toolbar changes
 | 
			
		||||
                var changed = (state || []).map(function (value, index) {
 | 
			
		||||
                    return toolbar.updateState(index, value);
 | 
			
		||||
                    return self.toolbar.updateState(index, value);
 | 
			
		||||
                }).reduce(function (a, b) {
 | 
			
		||||
                    return a || b;
 | 
			
		||||
                }, false);
 | 
			
		||||
@@ -85,66 +91,73 @@ define(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Initialize toolbar (expose object to parent scope)
 | 
			
		||||
            function initialize(definition) {
 | 
			
		||||
                // If we have been asked to expose toolbar state...
 | 
			
		||||
                if (attrs.toolbar) {
 | 
			
		||||
                    // Initialize toolbar object
 | 
			
		||||
                    toolbar = new EditToolbar(definition, commit);
 | 
			
		||||
                    // Ensure toolbar state is exposed
 | 
			
		||||
                    scope.$parent[attrs.toolbar] = toolbarObject;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Represent a domain object using this definition
 | 
			
		||||
            function represent(representation) {
 | 
			
		||||
                // Get the newest toolbar definition from the view
 | 
			
		||||
                var definition = (representation || {}).toolbar || {};
 | 
			
		||||
                // Expose the toolbar object to the parent scope
 | 
			
		||||
                initialize(definition);
 | 
			
		||||
                // Create a selection scope
 | 
			
		||||
                scope.selection = new EditToolbarSelection();
 | 
			
		||||
                // Initialize toolbar to an empty selection
 | 
			
		||||
                updateSelection([]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Destroy; remove toolbar object from parent scope
 | 
			
		||||
            function destroy() {
 | 
			
		||||
            // Avoid attaching scope to this;
 | 
			
		||||
            // http://errors.angularjs.org/1.2.26/ng/cpws
 | 
			
		||||
            this.setSelection = function (s) {
 | 
			
		||||
                scope.selection = s;
 | 
			
		||||
            };
 | 
			
		||||
            this.clearExposedToolbar = function () {
 | 
			
		||||
                // Clear exposed toolbar state (if any)
 | 
			
		||||
                if (attrs.toolbar) {
 | 
			
		||||
                    delete scope.$parent[attrs.toolbar];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            };
 | 
			
		||||
            this.exposeToolbar = function () {
 | 
			
		||||
                scope.$parent[self.attrs.toolbar] = self.toolbarObject;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.commit = commit;
 | 
			
		||||
            this.attrs = attrs;
 | 
			
		||||
            this.updateSelection = updateSelection;
 | 
			
		||||
            this.toolbar = undefined;
 | 
			
		||||
            this.toolbarObject = {};
 | 
			
		||||
 | 
			
		||||
            // If this representation exposes a toolbar, set up watches
 | 
			
		||||
            // to synchronize with it.
 | 
			
		||||
            if (attrs.toolbar) {
 | 
			
		||||
            if (attrs && attrs.toolbar) {
 | 
			
		||||
                // Detect and handle changes to state from the toolbar
 | 
			
		||||
                scope.$watchCollection(getState, updateState);
 | 
			
		||||
                // Watch for changes in the current selection state
 | 
			
		||||
                scope.$watchCollection("selection.all()", updateSelection);
 | 
			
		||||
                // Expose toolbar state under that name
 | 
			
		||||
                scope.$parent[attrs.toolbar] = toolbarObject;
 | 
			
		||||
                scope.$parent[attrs.toolbar] = this.toolbarObject;
 | 
			
		||||
            } else {
 | 
			
		||||
                // No toolbar declared, so do nothing.
 | 
			
		||||
                return NOOP_REPRESENTER;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Set the current representation in use, and the domain
 | 
			
		||||
                 * object being represented.
 | 
			
		||||
                 *
 | 
			
		||||
                 * @param {RepresentationDefinition} representation the
 | 
			
		||||
                 *        definition of the representation in use
 | 
			
		||||
                 * @param {DomainObject} domainObject the domain object
 | 
			
		||||
                 *        being represented
 | 
			
		||||
                 */
 | 
			
		||||
                represent: (attrs || {}).toolbar ? represent : noop,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Release any resources associated with this representer.
 | 
			
		||||
                 */
 | 
			
		||||
                destroy: (attrs || {}).toolbar ? destroy : noop
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Represent a domain object using this definition
 | 
			
		||||
        EditToolbarRepresenter.prototype.represent = function (representation) {
 | 
			
		||||
            // Get the newest toolbar definition from the view
 | 
			
		||||
            var definition = (representation || {}).toolbar || {},
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            // Initialize toolbar (expose object to parent scope)
 | 
			
		||||
            function initialize(definition) {
 | 
			
		||||
                // If we have been asked to expose toolbar state...
 | 
			
		||||
                if (self.attrs.toolbar) {
 | 
			
		||||
                    // Initialize toolbar object
 | 
			
		||||
                    self.toolbar = new EditToolbar(definition, self.commit);
 | 
			
		||||
                    // Ensure toolbar state is exposed
 | 
			
		||||
                    self.exposeToolbar();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Expose the toolbar object to the parent scope
 | 
			
		||||
            initialize(definition);
 | 
			
		||||
            // Create a selection scope
 | 
			
		||||
            this.setSelection(new EditToolbarSelection());
 | 
			
		||||
            // Initialize toolbar to an empty selection
 | 
			
		||||
            this.updateSelection([]);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Destroy; remove toolbar object from parent scope
 | 
			
		||||
        EditToolbarRepresenter.prototype.destroy = function () {
 | 
			
		||||
            this.clearExposedToolbar();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditToolbarRepresenter;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,110 +37,96 @@ define(
 | 
			
		||||
         * * The selection, for single selected elements within the
 | 
			
		||||
         *   view.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/edit
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditToolbarSelection() {
 | 
			
		||||
            var selection = [ {} ],
 | 
			
		||||
                selecting = false,
 | 
			
		||||
                selected;
 | 
			
		||||
            this.selection = [{}];
 | 
			
		||||
            this.selecting = false;
 | 
			
		||||
            this.selectedObj = undefined;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Remove the currently-selected object
 | 
			
		||||
            function deselect() {
 | 
			
		||||
                // Nothing to do if we don't have a selected object
 | 
			
		||||
                if (selecting) {
 | 
			
		||||
                    // Clear state tracking
 | 
			
		||||
                    selecting = false;
 | 
			
		||||
                    selected = undefined;
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if an object is currently selected.
 | 
			
		||||
         * @param {*} obj the object to check for selection
 | 
			
		||||
         * @returns {boolean} true if selected, otherwise false
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.selected = function (obj) {
 | 
			
		||||
            return (obj === this.selectedObj) || (obj === this.selection[0]);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
                    // Remove the selection
 | 
			
		||||
                    selection.pop();
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
        /**
 | 
			
		||||
         * Select an object.
 | 
			
		||||
         * @param obj the object to select
 | 
			
		||||
         * @returns {boolean} true if selection changed
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.select = function (obj) {
 | 
			
		||||
            // Proxy is always selected
 | 
			
		||||
            if (obj === this.selection[0]) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Select an object
 | 
			
		||||
            function select(obj) {
 | 
			
		||||
                // Proxy is always selected
 | 
			
		||||
                if (obj === selection[0]) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            // Clear any existing selection
 | 
			
		||||
            this.deselect();
 | 
			
		||||
 | 
			
		||||
                // Clear any existing selection
 | 
			
		||||
                deselect();
 | 
			
		||||
            // Note the current selection state
 | 
			
		||||
            this.selectedObj = obj;
 | 
			
		||||
            this.selecting = true;
 | 
			
		||||
 | 
			
		||||
                // Note the current selection state
 | 
			
		||||
                selected = obj;
 | 
			
		||||
                selecting = true;
 | 
			
		||||
            // Add the selection
 | 
			
		||||
            this.selection.push(obj);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
                // Add the selection
 | 
			
		||||
                selection.push(obj);
 | 
			
		||||
        /**
 | 
			
		||||
         * Clear the current selection.
 | 
			
		||||
         * @returns {boolean} true if selection changed
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.deselect = function () {
 | 
			
		||||
            // Nothing to do if we don't have a selected object
 | 
			
		||||
            if (this.selecting) {
 | 
			
		||||
                // Clear state tracking
 | 
			
		||||
                this.selecting = false;
 | 
			
		||||
                this.selectedObj = undefined;
 | 
			
		||||
 | 
			
		||||
                // Remove the selection
 | 
			
		||||
                this.selection.pop();
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the currently-selected object.
 | 
			
		||||
         * @returns the currently selected object
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.get = function () {
 | 
			
		||||
            return this.selectedObj;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            // Check if an object is selected
 | 
			
		||||
            function isSelected(obj) {
 | 
			
		||||
                return (obj === selected) || (obj === selection[0]);
 | 
			
		||||
        /**
 | 
			
		||||
         * Get/set the view proxy (for toolbar actions taken upon
 | 
			
		||||
         * the view itself.)
 | 
			
		||||
         * @param [proxy] the view proxy (if setting)
 | 
			
		||||
         * @returns the current view proxy
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.proxy = function (p) {
 | 
			
		||||
            if (arguments.length > 0) {
 | 
			
		||||
                this.selection[0] = p;
 | 
			
		||||
            }
 | 
			
		||||
            return this.selection[0];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            // Getter for current selection
 | 
			
		||||
            function get() {
 | 
			
		||||
                return selected;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Getter/setter for view proxy
 | 
			
		||||
            function proxy(p) {
 | 
			
		||||
                if (arguments.length > 0) {
 | 
			
		||||
                    selection[0] = p;
 | 
			
		||||
                }
 | 
			
		||||
                return selection[0];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Getter for the full array of selected objects (incl. view proxy)
 | 
			
		||||
            function all() {
 | 
			
		||||
                return selection;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                /**
 | 
			
		||||
                 * Check if an object is currently selected.
 | 
			
		||||
                 * @returns true if selected, otherwise false
 | 
			
		||||
                 */
 | 
			
		||||
                selected: isSelected,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Select an object.
 | 
			
		||||
                 * @param obj the object to select
 | 
			
		||||
                 * @returns {boolean} true if selection changed
 | 
			
		||||
                 */
 | 
			
		||||
                select: select,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Clear the current selection.
 | 
			
		||||
                 * @returns {boolean} true if selection changed
 | 
			
		||||
                 */
 | 
			
		||||
                deselect: deselect,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get the currently-selected object.
 | 
			
		||||
                 * @returns the currently selected object
 | 
			
		||||
                 */
 | 
			
		||||
                get: get,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get/set the view proxy (for toolbar actions taken upon
 | 
			
		||||
                 * the view itself.)
 | 
			
		||||
                 * @param [proxy] the view proxy (if setting)
 | 
			
		||||
                 * @returns the current view proxy
 | 
			
		||||
                 */
 | 
			
		||||
                proxy: proxy,
 | 
			
		||||
                /**
 | 
			
		||||
                 * Get an array containing all selections, including the
 | 
			
		||||
                 * selection proxy. It is generally not advisable to
 | 
			
		||||
                 * mutate this array directly.
 | 
			
		||||
                 * @returns {Array} all selections
 | 
			
		||||
                 */
 | 
			
		||||
                all: all
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        /**
 | 
			
		||||
         * Get an array containing all selections, including the
 | 
			
		||||
         * selection proxy. It is generally not advisable to
 | 
			
		||||
         * mutate this array directly.
 | 
			
		||||
         * @returns {Array} all selections
 | 
			
		||||
         */
 | 
			
		||||
        EditToolbarSelection.prototype.all = function () {
 | 
			
		||||
            return this.selection;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditToolbarSelection;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ define(
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockParent,
 | 
			
		||||
                mockContext,
 | 
			
		||||
                mockMutation,
 | 
			
		||||
                mockComposition,
 | 
			
		||||
                mockPersistence,
 | 
			
		||||
                mockType,
 | 
			
		||||
                actionContext,
 | 
			
		||||
@@ -67,7 +67,7 @@ define(
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                mockContext = jasmine.createSpyObj("context", [ "getParent" ]);
 | 
			
		||||
                mockMutation = jasmine.createSpyObj("mutation", [ "invoke" ]);
 | 
			
		||||
                mockComposition = jasmine.createSpyObj("composition", [ "invoke", "add" ]);
 | 
			
		||||
                mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]);
 | 
			
		||||
                mockType = jasmine.createSpyObj("type", [ "hasFeature" ]);
 | 
			
		||||
 | 
			
		||||
@@ -75,11 +75,11 @@ define(
 | 
			
		||||
                mockDomainObject.getCapability.andReturn(mockContext);
 | 
			
		||||
                mockContext.getParent.andReturn(mockParent);
 | 
			
		||||
                mockType.hasFeature.andReturn(true);
 | 
			
		||||
                mockMutation.invoke.andReturn(mockPromise(true));
 | 
			
		||||
 | 
			
		||||
                mockComposition.invoke.andReturn(mockPromise(true));
 | 
			
		||||
                mockComposition.add.andReturn(mockPromise(true));
 | 
			
		||||
 | 
			
		||||
                capabilities = {
 | 
			
		||||
                    mutation: mockMutation,
 | 
			
		||||
                    composition: mockComposition,
 | 
			
		||||
                    persistence: mockPersistence,
 | 
			
		||||
                    type: mockType
 | 
			
		||||
                };
 | 
			
		||||
@@ -96,33 +96,17 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("mutates the parent when performed", function () {
 | 
			
		||||
            it("adds to the parent's composition when performed", function () {
 | 
			
		||||
                action.perform();
 | 
			
		||||
                expect(mockMutation.invoke)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(Function));
 | 
			
		||||
                expect(mockComposition.add)
 | 
			
		||||
                    .toHaveBeenCalledWith(mockDomainObject);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("changes composition from its mutation function", function () {
 | 
			
		||||
                var mutator, result;
 | 
			
		||||
            it("persists changes afterward", function () {
 | 
			
		||||
                action.perform();
 | 
			
		||||
                mutator = mockMutation.invoke.mostRecentCall.args[0];
 | 
			
		||||
                result = mutator(model);
 | 
			
		||||
 | 
			
		||||
                // Should not have cancelled the mutation
 | 
			
		||||
                expect(result).not.toBe(false);
 | 
			
		||||
 | 
			
		||||
                // Simulate mutate's behavior (remove can either return a
 | 
			
		||||
                // new model or modify this one in-place)
 | 
			
		||||
                result = result || model;
 | 
			
		||||
 | 
			
		||||
                // Should have removed "test" - that was our
 | 
			
		||||
                // mock domain object's id.
 | 
			
		||||
                expect(result.composition).toEqual(["a", "b", "c", "test"]);
 | 
			
		||||
 | 
			
		||||
                // Finally, should have persisted
 | 
			
		||||
                expect(mockPersistence.persist).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ define(
 | 
			
		||||
                    return {
 | 
			
		||||
                        getValue: function (model) { return model[k]; },
 | 
			
		||||
                        setValue: function (model, v) { model[k] = v; },
 | 
			
		||||
                        getDefinition: function () { return {}; }
 | 
			
		||||
                        getDefinition: function () { return { control: 'textfield '}; }
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,16 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The Remove action", function () {
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockNavigationService,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockParent,
 | 
			
		||||
                mockChildObject,
 | 
			
		||||
                mockGrandchildObject,
 | 
			
		||||
                mockRootObject,
 | 
			
		||||
                mockContext,
 | 
			
		||||
                mockChildContext,
 | 
			
		||||
                mockGrandchildContext,
 | 
			
		||||
                mockRootContext,
 | 
			
		||||
                mockMutation,
 | 
			
		||||
                mockPersistence,
 | 
			
		||||
                mockType,
 | 
			
		||||
@@ -54,6 +61,18 @@ define(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getId", "getCapability" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockChildObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getId", "getCapability" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockGrandchildObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getId", "getCapability" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockRootObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getId", "getCapability" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockQ = { when: mockPromise };
 | 
			
		||||
                mockParent = {
 | 
			
		||||
                    getModel: function () {
 | 
			
		||||
@@ -67,28 +86,41 @@ define(
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                mockContext = jasmine.createSpyObj("context", [ "getParent" ]);
 | 
			
		||||
                mockChildContext = jasmine.createSpyObj("context", [ "getParent" ]);
 | 
			
		||||
                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",
 | 
			
		||||
                    [
 | 
			
		||||
                        "getNavigation",
 | 
			
		||||
                        "setNavigation",
 | 
			
		||||
                        "addListener",
 | 
			
		||||
                        "removeListener"
 | 
			
		||||
                    ]
 | 
			
		||||
                );
 | 
			
		||||
                mockNavigationService.getNavigation.andReturn(mockDomainObject);
 | 
			
		||||
                
 | 
			
		||||
                
 | 
			
		||||
                mockDomainObject.getId.andReturn("test");
 | 
			
		||||
                mockDomainObject.getCapability.andReturn(mockContext);
 | 
			
		||||
                mockContext.getParent.andReturn(mockParent);
 | 
			
		||||
                mockType.hasFeature.andReturn(true);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                capabilities = {
 | 
			
		||||
                    mutation: mockMutation,
 | 
			
		||||
                    persistence: mockPersistence,
 | 
			
		||||
                    type: mockType
 | 
			
		||||
                };
 | 
			
		||||
                model = {
 | 
			
		||||
                    composition: [ "a", "test", "b", "c" ]
 | 
			
		||||
                    composition: [ "a", "test", "b" ]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                actionContext = { domainObject: mockDomainObject };
 | 
			
		||||
 | 
			
		||||
                action = new RemoveAction(mockQ, actionContext);
 | 
			
		||||
                action = new RemoveAction(mockQ, mockNavigationService, actionContext);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("only applies to objects with parents", function () {
 | 
			
		||||
@@ -123,11 +155,64 @@ define(
 | 
			
		||||
 | 
			
		||||
                // Should have removed "test" - that was our
 | 
			
		||||
                // mock domain object's id.
 | 
			
		||||
                expect(result.composition).toEqual(["a", "b", "c"]);
 | 
			
		||||
                expect(result.composition).toEqual(["a", "b"]);
 | 
			
		||||
 | 
			
		||||
                // Finally, should have persisted
 | 
			
		||||
                expect(mockPersistence.persist).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("removes parent of object currently navigated to", function () {
 | 
			
		||||
                // Navigates to child object
 | 
			
		||||
                mockNavigationService.getNavigation.andReturn(mockChildObject);
 | 
			
		||||
                
 | 
			
		||||
                // Test is id of object being removed
 | 
			
		||||
                // Child object has different id
 | 
			
		||||
                mockDomainObject.getId.andReturn("test");
 | 
			
		||||
                mockChildObject.getId.andReturn("not test");
 | 
			
		||||
                
 | 
			
		||||
                // Sets context for the child and domainObject
 | 
			
		||||
                mockDomainObject.getCapability.andReturn(mockContext);
 | 
			
		||||
                mockChildObject.getCapability.andReturn(mockChildContext);
 | 
			
		||||
                
 | 
			
		||||
                // Parents of child and domainObject are set
 | 
			
		||||
                mockContext.getParent.andReturn(mockParent);
 | 
			
		||||
                mockChildContext.getParent.andReturn(mockDomainObject);
 | 
			
		||||
                
 | 
			
		||||
                mockType.hasFeature.andReturn(true);
 | 
			
		||||
                
 | 
			
		||||
                action.perform();
 | 
			
		||||
                
 | 
			
		||||
                // Expects navigation to parent of domainObject (removed object)
 | 
			
		||||
                expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent);
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("checks if removing object not in ascendent path (reaches ROOT)", function () {
 | 
			
		||||
                // Navigates to grandchild of ROOT
 | 
			
		||||
                mockNavigationService.getNavigation.andReturn(mockGrandchildObject);
 | 
			
		||||
                
 | 
			
		||||
                // domainObject (grandparent) is set as ROOT, child and grandchild
 | 
			
		||||
                // are set objects not being removed
 | 
			
		||||
                mockDomainObject.getId.andReturn("test 1");
 | 
			
		||||
                mockRootObject.getId.andReturn("ROOT");
 | 
			
		||||
                mockChildObject.getId.andReturn("not test 2");
 | 
			
		||||
                mockGrandchildObject.getId.andReturn("not test 3");
 | 
			
		||||
                
 | 
			
		||||
                // Sets context for the grandchild, child, and domainObject
 | 
			
		||||
                mockRootObject.getCapability.andReturn(mockRootContext);
 | 
			
		||||
                mockChildObject.getCapability.andReturn(mockChildContext);
 | 
			
		||||
                mockGrandchildObject.getCapability.andReturn(mockGrandchildContext);
 | 
			
		||||
                
 | 
			
		||||
                // Parents of grandchild and child are set
 | 
			
		||||
                mockChildContext.getParent.andReturn(mockRootObject);
 | 
			
		||||
                mockGrandchildContext.getParent.andReturn(mockChildObject);
 | 
			
		||||
                
 | 
			
		||||
                mockType.hasFeature.andReturn(true);
 | 
			
		||||
                
 | 
			
		||||
                action.perform();
 | 
			
		||||
                
 | 
			
		||||
                // Expects no navigation to occur
 | 
			
		||||
                expect(mockNavigationService.setNavigation).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -112,7 +112,9 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("saves objects that have been marked dirty", function () {
 | 
			
		||||
                var objects = ['a', 'b', 'c'].map(TestObject).map(cache.getEditableObject);
 | 
			
		||||
                var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
 | 
			
		||||
                    return cache.getEditableObject(domainObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                cache.markDirty(objects[0]);
 | 
			
		||||
                cache.markDirty(objects[2]);
 | 
			
		||||
@@ -123,7 +125,9 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not save objects that have been marked clean", function () {
 | 
			
		||||
                var objects = ['a', 'b', 'c'].map(TestObject).map(cache.getEditableObject);
 | 
			
		||||
                var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
 | 
			
		||||
                    return cache.getEditableObject(domainObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                cache.markDirty(objects[0]);
 | 
			
		||||
                cache.markDirty(objects[2]);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,13 @@
 | 
			
		||||
        "services": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "urlService",
 | 
			
		||||
                "implementation": "/services/UrlService.js",
 | 
			
		||||
                "implementation": "services/UrlService.js",
 | 
			
		||||
                "depends": [ "$location" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "popupService",
 | 
			
		||||
                "implementation": "services/PopupService.js",
 | 
			
		||||
                "depends": [ "$document", "$window" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "runs": [
 | 
			
		||||
@@ -20,18 +25,6 @@
 | 
			
		||||
            {
 | 
			
		||||
                "stylesheetUrl": "css/normalize.min.css",
 | 
			
		||||
                "priority": "mandatory"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "stylesheetUrl": "css/theme-espresso.css",
 | 
			
		||||
                "priority": 1000
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "stylesheetUrl": "css/items.css",
 | 
			
		||||
                "priority": 901
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "stylesheetUrl": "css/tree.css",
 | 
			
		||||
                "priority": 900
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "templates": [
 | 
			
		||||
@@ -57,6 +50,16 @@
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TimeRangeController",
 | 
			
		||||
                "implementation": "controllers/TimeRangeController.js",
 | 
			
		||||
                "depends": [ "$scope", "now" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DateTimePickerController",
 | 
			
		||||
                "implementation": "controllers/DateTimePickerController.js",
 | 
			
		||||
                "depends": [ "$scope", "now" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "TreeNodeController",
 | 
			
		||||
                "implementation": "controllers/TreeNodeController.js",
 | 
			
		||||
@@ -117,11 +120,21 @@
 | 
			
		||||
                "implementation": "directives/MCTDrag.js",
 | 
			
		||||
                "depends": [ "$document" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctClickElsewhere",
 | 
			
		||||
                "implementation": "directives/MCTClickElsewhere.js",
 | 
			
		||||
                "depends": [ "$document" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctResize",
 | 
			
		||||
                "implementation": "directives/MCTResize.js",
 | 
			
		||||
                "depends": [ "$timeout" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctPopup",
 | 
			
		||||
                "implementation": "directives/MCTPopup.js",
 | 
			
		||||
                "depends": [ "$compile", "popupService" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctScrollX",
 | 
			
		||||
                "implementation": "directives/MCTScroll.js",
 | 
			
		||||
@@ -135,7 +148,7 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctSplitPane",
 | 
			
		||||
                "implementation": "directives/MCTSplitPane.js",
 | 
			
		||||
                "depends": [ "$parse", "$log" ]
 | 
			
		||||
                "depends": [ "$parse", "$log", "$interval" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctSplitter",
 | 
			
		||||
@@ -196,7 +209,7 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "label",
 | 
			
		||||
                "templateUrl": "templates/label.html",
 | 
			
		||||
                "uses": [ "type" ],
 | 
			
		||||
                "uses": [ "type", "location" ],
 | 
			
		||||
                "gestures": [ "drag", "menu", "info" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -225,6 +238,10 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "selector",
 | 
			
		||||
                "templateUrl": "templates/controls/selector.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "datetime-picker",
 | 
			
		||||
                "templateUrl": "templates/controls/datetime-picker.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "licenses": [
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ javascripts_dir = "js"
 | 
			
		||||
output_style = :nested
 | 
			
		||||
 | 
			
		||||
# To enable relative paths to assets via compass helper functions. Uncomment:
 | 
			
		||||
# relative_assets = true
 | 
			
		||||
relative_assets = true
 | 
			
		||||
 | 
			
		||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
 | 
			
		||||
# line_comments = false
 | 
			
		||||
 
 | 
			
		||||
@@ -1,541 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/************************** FEATURES */
 | 
			
		||||
/************************** VERY INFLUENTIAL GLOBAL DIMENSIONS */
 | 
			
		||||
/************************** COLORS AND SHADING */
 | 
			
		||||
/************************** RATIOS */
 | 
			
		||||
/************************** LAYOUT */
 | 
			
		||||
/************************** CONTROLS */
 | 
			
		||||
/************************** PATHS */
 | 
			
		||||
/************************** TIMINGS */
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*
 | 
			
		||||
@mixin invokeMenu($baseColor: $colorBodyFg) {
 | 
			
		||||
	$c: $baseColor;
 | 
			
		||||
	color: $c;
 | 
			
		||||
	&:hover {
 | 
			
		||||
		color: lighten($c, $ltGamma);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 22, ../sass/forms/_elems.scss */
 | 
			
		||||
.section-header {
 | 
			
		||||
  -moz-border-radius: 3px;
 | 
			
		||||
  -webkit-border-radius: 3px;
 | 
			
		||||
  border-radius: 3px;
 | 
			
		||||
  background: rgba(255, 255, 255, 0.1);
 | 
			
		||||
  font-size: 0.8em;
 | 
			
		||||
  margin-top: 5px;
 | 
			
		||||
  padding: 5px; }
 | 
			
		||||
  /* line 28, ../sass/forms/_elems.scss */
 | 
			
		||||
  .section-header:first-child {
 | 
			
		||||
    margin-top: 0; }
 | 
			
		||||
 | 
			
		||||
/* line 35, ../sass/forms/_elems.scss */
 | 
			
		||||
.form .form-section {
 | 
			
		||||
  position: relative; }
 | 
			
		||||
/* line 39, ../sass/forms/_elems.scss */
 | 
			
		||||
.form .form-row {
 | 
			
		||||
  -moz-box-sizing: border-box;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  *zoom: 1;
 | 
			
		||||
  border-top: 1px solid #4d4d4d;
 | 
			
		||||
  margin-top: 5px;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
  position: relative; }
 | 
			
		||||
  /* line 46, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row.first {
 | 
			
		||||
    border-top: none; }
 | 
			
		||||
  /* line 50, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row .label,
 | 
			
		||||
  .form .form-row .controls {
 | 
			
		||||
    -moz-box-sizing: border-box;
 | 
			
		||||
    -webkit-box-sizing: border-box;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    *zoom: 1;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    font-size: 0.75rem;
 | 
			
		||||
    line-height: 22px;
 | 
			
		||||
    min-height: 22px; }
 | 
			
		||||
  /* line 61, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row > .label {
 | 
			
		||||
    float: left;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    width: 20%; }
 | 
			
		||||
  /* line 69, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row .value {
 | 
			
		||||
    color: #cccccc; }
 | 
			
		||||
  /* line 73, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row .controls {
 | 
			
		||||
    float: left;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 79.9%; }
 | 
			
		||||
    /* line 80, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls .l-composite-control.l-checkbox {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      line-height: 14px;
 | 
			
		||||
      margin-right: 5px; }
 | 
			
		||||
    /* line 89, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls input[type="text"] {
 | 
			
		||||
      height: 22px;
 | 
			
		||||
      line-height: 22px;
 | 
			
		||||
      margin-top: -4px;
 | 
			
		||||
      vertical-align: baseline; }
 | 
			
		||||
    /* line 96, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls .l-med input[type="text"] {
 | 
			
		||||
      width: 200px; }
 | 
			
		||||
    /* line 100, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls .l-small input[type="text"] {
 | 
			
		||||
      width: 50px; }
 | 
			
		||||
    /* line 104, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls .l-numeric input[type="text"] {
 | 
			
		||||
      text-align: right; }
 | 
			
		||||
    /* line 108, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .controls .select {
 | 
			
		||||
      margin-right: 5px; }
 | 
			
		||||
  /* line 113, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row .field-hints {
 | 
			
		||||
    color: #666666; }
 | 
			
		||||
  /* line 117, ../sass/forms/_elems.scss */
 | 
			
		||||
  .form .form-row .selector-list {
 | 
			
		||||
    -moz-border-radius: 2px;
 | 
			
		||||
    -webkit-border-radius: 2px;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    -moz-box-sizing: border-box;
 | 
			
		||||
    -webkit-box-sizing: border-box;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    background: rgba(0, 0, 0, 0.2);
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    height: 150px;
 | 
			
		||||
    overflow: auto; }
 | 
			
		||||
    /* line 128, ../sass/forms/_elems.scss */
 | 
			
		||||
    .form .form-row .selector-list .wrapper {
 | 
			
		||||
      overflow-y: auto;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 5px;
 | 
			
		||||
      right: 5px;
 | 
			
		||||
      bottom: 5px;
 | 
			
		||||
      left: 5px; }
 | 
			
		||||
 | 
			
		||||
/* line 142, ../sass/forms/_elems.scss */
 | 
			
		||||
label.form-control.checkbox input {
 | 
			
		||||
  margin-right: 5px;
 | 
			
		||||
  vertical-align: top; }
 | 
			
		||||
 | 
			
		||||
/* line 148, ../sass/forms/_elems.scss */
 | 
			
		||||
.hint,
 | 
			
		||||
.s-hint {
 | 
			
		||||
  font-size: 0.9em; }
 | 
			
		||||
 | 
			
		||||
/* line 153, ../sass/forms/_elems.scss */
 | 
			
		||||
.l-result {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  min-width: 32px;
 | 
			
		||||
  min-height: 32px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  vertical-align: top; }
 | 
			
		||||
  /* line 160, ../sass/forms/_elems.scss */
 | 
			
		||||
  .l-result div.s-hint {
 | 
			
		||||
    -moz-border-radius: 2px;
 | 
			
		||||
    -webkit-border-radius: 2px;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    background: rgba(255, 153, 0, 0.8);
 | 
			
		||||
    display: block;
 | 
			
		||||
    color: #ffd699;
 | 
			
		||||
    padding: 5px; }
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 22, ../sass/forms/_textarea.scss */
 | 
			
		||||
.edit-main textarea {
 | 
			
		||||
  -moz-appearance: none;
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  -moz-border-radius: 2px;
 | 
			
		||||
  -webkit-border-radius: 2px;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  -moz-box-sizing: border-box;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  -moz-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  -webkit-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  background: rgba(255, 255, 255, 0.1);
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
 | 
			
		||||
  color: #cccccc;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 100%; }
 | 
			
		||||
  /* line 33, ../sass/forms/_mixins.scss */
 | 
			
		||||
  .edit-main textarea.error {
 | 
			
		||||
    background: rgba(255, 0, 0, 0.5); }
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 22, ../sass/forms/_text-input.scss */
 | 
			
		||||
input[type="text"] {
 | 
			
		||||
  -moz-appearance: none;
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  -moz-border-radius: 2px;
 | 
			
		||||
  -webkit-border-radius: 2px;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  -moz-box-sizing: border-box;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  -moz-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  -webkit-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  background: rgba(255, 255, 255, 0.1);
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
 | 
			
		||||
  color: #cccccc;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  padding: 0 3px; }
 | 
			
		||||
  /* line 33, ../sass/forms/_mixins.scss */
 | 
			
		||||
  input[type="text"].error {
 | 
			
		||||
    background: rgba(255, 0, 0, 0.5); }
 | 
			
		||||
  /* line 29, ../sass/forms/_text-input.scss */
 | 
			
		||||
  input[type="text"].numeric {
 | 
			
		||||
    text-align: right; }
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 22, ../sass/forms/_selects.scss */
 | 
			
		||||
.form-control.select {
 | 
			
		||||
  background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzQ1NDU0NSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
 | 
			
		||||
  background-size: 100%;
 | 
			
		||||
  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #525252), color-stop(100%, #454545));
 | 
			
		||||
  background-image: -moz-linear-gradient(#525252, #454545);
 | 
			
		||||
  background-image: -webkit-linear-gradient(#525252, #454545);
 | 
			
		||||
  background-image: linear-gradient(#525252, #454545);
 | 
			
		||||
  -moz-border-radius: 2px;
 | 
			
		||||
  -webkit-border-radius: 2px;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  -moz-box-sizing: border-box;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
 | 
			
		||||
  -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
 | 
			
		||||
  box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px;
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-top: 1px solid #575757;
 | 
			
		||||
  color: #999;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin: 0 0 2px 2px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: relative; }
 | 
			
		||||
  /* line 162, ../sass/_mixins.scss */
 | 
			
		||||
  .form-control.select:not(.disabled):hover {
 | 
			
		||||
    background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzYzNjM2MyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzU3NTc1NyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
 | 
			
		||||
    background-size: 100%;
 | 
			
		||||
    background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #636363), color-stop(100%, #575757));
 | 
			
		||||
    background-image: -moz-linear-gradient(#636363, #575757);
 | 
			
		||||
    background-image: -webkit-linear-gradient(#636363, #575757);
 | 
			
		||||
    background-image: linear-gradient(#636363, #575757);
 | 
			
		||||
    color: #bdbdbd; }
 | 
			
		||||
    /* line 165, ../sass/_mixins.scss */
 | 
			
		||||
    .form-control.select:not(.disabled):hover.btn-menu .invoke-menu {
 | 
			
		||||
      color: #878787; }
 | 
			
		||||
  /* line 170, ../sass/_mixins.scss */
 | 
			
		||||
  .form-control.select.btn-menu .invoke-menu {
 | 
			
		||||
    color: #757575; }
 | 
			
		||||
  /* line 29, ../sass/forms/_selects.scss */
 | 
			
		||||
  .form-control.select select {
 | 
			
		||||
    -moz-appearance: none;
 | 
			
		||||
    -webkit-appearance: none;
 | 
			
		||||
    -moz-box-sizing: border-box;
 | 
			
		||||
    -webkit-box-sizing: border-box;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    background: none;
 | 
			
		||||
    color: #999;
 | 
			
		||||
    border: none !important;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    padding: 4px 25px 2px 5px;
 | 
			
		||||
    width: 120%; }
 | 
			
		||||
    /* line 38, ../sass/forms/_selects.scss */
 | 
			
		||||
    .form-control.select select option {
 | 
			
		||||
      margin: 5px 0; }
 | 
			
		||||
  /* line 42, ../sass/forms/_selects.scss */
 | 
			
		||||
  .form-control.select:after {
 | 
			
		||||
    color: #0099cc;
 | 
			
		||||
    content: "v";
 | 
			
		||||
    display: block;
 | 
			
		||||
    font-family: 'symbolsfont';
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 5px;
 | 
			
		||||
    top: 0; }
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 23, ../sass/forms/_channel-selector.scss */
 | 
			
		||||
.channel-selector .line {
 | 
			
		||||
  margin-bottom: 5px;
 | 
			
		||||
  min-height: 22px; }
 | 
			
		||||
/* line 27, ../sass/forms/_channel-selector.scss */
 | 
			
		||||
.channel-selector .treeview {
 | 
			
		||||
  -moz-appearance: none;
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  -moz-border-radius: 2px;
 | 
			
		||||
  -webkit-border-radius: 2px;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  -moz-box-sizing: border-box;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  -moz-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  -webkit-box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  box-shadow: inset rgba(0, 0, 0, 0.5) 0 1px 5px;
 | 
			
		||||
  background: rgba(255, 255, 255, 0.1);
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
 | 
			
		||||
  color: #cccccc;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  padding: 0 3px;
 | 
			
		||||
  background: #3b3b3b;
 | 
			
		||||
  border-bottom: 1px solid #4d4d4d;
 | 
			
		||||
  min-height: 300px;
 | 
			
		||||
  max-height: 400px;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  padding: 5px; }
 | 
			
		||||
  /* line 33, ../sass/forms/_mixins.scss */
 | 
			
		||||
  .channel-selector .treeview.error {
 | 
			
		||||
    background: rgba(255, 0, 0, 0.5); }
 | 
			
		||||
/* line 36, ../sass/forms/_channel-selector.scss */
 | 
			
		||||
.channel-selector .btns-add-remove {
 | 
			
		||||
  margin-top: 150px; }
 | 
			
		||||
  /* line 39, ../sass/forms/_channel-selector.scss */
 | 
			
		||||
  .channel-selector .btns-add-remove .btn {
 | 
			
		||||
    display: block;
 | 
			
		||||
    font-size: 1.5em;
 | 
			
		||||
    margin-bottom: 5px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    text-align: center; }
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/* line 23, ../sass/forms/_datetime.scss */
 | 
			
		||||
.complex.datetime span {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-right: 5px; }
 | 
			
		||||
/* line 36, ../sass/forms/_datetime.scss */
 | 
			
		||||
.complex.datetime .fields {
 | 
			
		||||
  margin-top: 3px 0;
 | 
			
		||||
  padding: 3px 0; }
 | 
			
		||||
/* line 41, ../sass/forms/_datetime.scss */
 | 
			
		||||
.complex.datetime .date {
 | 
			
		||||
  width: 85px; }
 | 
			
		||||
  /* line 44, ../sass/forms/_datetime.scss */
 | 
			
		||||
  .complex.datetime .date input {
 | 
			
		||||
    width: 80px; }
 | 
			
		||||
/* line 50, ../sass/forms/_datetime.scss */
 | 
			
		||||
.complex.datetime .time.sm {
 | 
			
		||||
  width: 45px; }
 | 
			
		||||
  /* line 53, ../sass/forms/_datetime.scss */
 | 
			
		||||
  .complex.datetime .time.sm input {
 | 
			
		||||
    width: 40px; }
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user