mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Compare commits
	
		
			933 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3169f93cc2 | ||
| 
						 | 
					c1a1a73599 | ||
| 
						 | 
					db1b0ccb79 | ||
| 
						 | 
					d373105b32 | ||
| 
						 | 
					28b311b7ed | ||
| 
						 | 
					dcda513901 | ||
| 
						 | 
					72c400794c | ||
| 
						 | 
					042409f870 | ||
| 
						 | 
					5b8f4f4069 | ||
| 
						 | 
					d132d63c1d | ||
| 
						 | 
					ef8b936069 | ||
| 
						 | 
					36e3bfffb4 | ||
| 
						 | 
					91a38bdb60 | ||
| 
						 | 
					f169a68319 | ||
| 
						 | 
					ee886f98dd | ||
| 
						 | 
					a3826cc6a7 | ||
| 
						 | 
					ba33b832ba | ||
| 
						 | 
					f6c017176b | ||
| 
						 | 
					7a01b115bb | ||
| 
						 | 
					1dc021e871 | ||
| 
						 | 
					c9f916ebab | ||
| 
						 | 
					ff627fd128 | ||
| 
						 | 
					dfa077fd5f | ||
| 
						 | 
					5155770213 | ||
| 
						 | 
					f1d5bbb036 | ||
| 
						 | 
					69ed0aebc3 | ||
| 
						 | 
					549e56e220 | ||
| 
						 | 
					450f4d9a5a | ||
| 
						 | 
					6533a9793c | ||
| 
						 | 
					2000cadb17 | ||
| 
						 | 
					083c321efa | ||
| 
						 | 
					f64c4a981f | ||
| 
						 | 
					3ac8ce03bf | ||
| 
						 | 
					66fca8710e | ||
| 
						 | 
					1e245ece46 | ||
| 
						 | 
					81efce03ba | ||
| 
						 | 
					4e549dd426 | ||
| 
						 | 
					52f74ff7e0 | ||
| 
						 | 
					3c71b815f5 | ||
| 
						 | 
					9efd48fe51 | ||
| 
						 | 
					4609ee75b6 | ||
| 
						 | 
					963ea4177e | ||
| 
						 | 
					17e6940a42 | ||
| 
						 | 
					315a9ceba3 | ||
| 
						 | 
					a2bdeedb09 | ||
| 
						 | 
					da5700d2d7 | ||
| 
						 | 
					90e7f30247 | ||
| 
						 | 
					3ccf6ba892 | ||
| 
						 | 
					e50cd5b745 | ||
| 
						 | 
					db77be5d72 | ||
| 
						 | 
					c36870c23e | ||
| 
						 | 
					e9be007040 | ||
| 
						 | 
					9e400d9aa6 | ||
| 
						 | 
					490c8dae75 | ||
| 
						 | 
					3bcffe375d | ||
| 
						 | 
					9f81a591e1 | ||
| 
						 | 
					3db5306c70 | ||
| 
						 | 
					45029dd084 | ||
| 
						 | 
					b01cd30339 | ||
| 
						 | 
					09329e1104 | ||
| 
						 | 
					ab0fc2ecfa | ||
| 
						 | 
					bf5d36d6bd | ||
| 
						 | 
					a29527ec96 | ||
| 
						 | 
					45e7ad8049 | ||
| 
						 | 
					4d54663efd | ||
| 
						 | 
					29d386cc51 | ||
| 
						 | 
					ba1a67969b | ||
| 
						 | 
					390ea5419e | ||
| 
						 | 
					0fdeec7cc4 | ||
| 
						 | 
					e34d883e50 | ||
| 
						 | 
					507871687b | ||
| 
						 | 
					ed58f62cd1 | ||
| 
						 | 
					94bc4e7125 | ||
| 
						 | 
					5832f7930d | ||
| 
						 | 
					0066a20c22 | ||
| 
						 | 
					774e4bfced | ||
| 
						 | 
					054c7a76a4 | ||
| 
						 | 
					c7f3b77aac | ||
| 
						 | 
					a6a4620374 | ||
| 
						 | 
					5148c62d1c | ||
| 
						 | 
					6fc863a91e | ||
| 
						 | 
					e066a154a1 | ||
| 
						 | 
					d432edaed2 | ||
| 
						 | 
					8f34f4e80b | ||
| 
						 | 
					39b751acf5 | ||
| 
						 | 
					bd5e8ba961 | ||
| 
						 | 
					ed20327c41 | ||
| 
						 | 
					991c68c394 | ||
| 
						 | 
					2acc31a4e7 | ||
| 
						 | 
					b9733e3dfa | ||
| 
						 | 
					bb106bfce7 | ||
| 
						 | 
					daf1388a6a | ||
| 
						 | 
					8226f1fa75 | ||
| 
						 | 
					e675512fa3 | ||
| 
						 | 
					65e67b6c3e | ||
| 
						 | 
					7612481570 | ||
| 
						 | 
					f6c7cb5804 | ||
| 
						 | 
					2201c9062f | ||
| 
						 | 
					ca3da262da | ||
| 
						 | 
					5847f92bef | ||
| 
						 | 
					31ee1be81e | ||
| 
						 | 
					6bccdd015f | ||
| 
						 | 
					cecea318da | ||
| 
						 | 
					8663ec6880 | ||
| 
						 | 
					9b03f128aa | ||
| 
						 | 
					8a51f97616 | ||
| 
						 | 
					ee74ed9ce9 | ||
| 
						 | 
					0f947c756e | ||
| 
						 | 
					cae7949a48 | ||
| 
						 | 
					be58b614e1 | ||
| 
						 | 
					b0a01fa4b2 | ||
| 
						 | 
					9734228001 | ||
| 
						 | 
					9df1d44bc4 | ||
| 
						 | 
					13d887028a | ||
| 
						 | 
					83a8979309 | ||
| 
						 | 
					75c29f1cb7 | ||
| 
						 | 
					d9d15e41c7 | ||
| 
						 | 
					d3598d5854 | ||
| 
						 | 
					3a8aaee5d7 | ||
| 
						 | 
					4fcf57d42c | ||
| 
						 | 
					adb0891335 | ||
| 
						 | 
					d21e719cc1 | ||
| 
						 | 
					5807ab82c1 | ||
| 
						 | 
					65cb04da63 | ||
| 
						 | 
					312e3611b1 | ||
| 
						 | 
					46acc62279 | ||
| 
						 | 
					529b358c9b | ||
| 
						 | 
					7fca04404e | ||
| 
						 | 
					3a1cc6a2be | ||
| 
						 | 
					5b76c91004 | ||
| 
						 | 
					cf87837f7d | ||
| 
						 | 
					5a0a7b907b | ||
| 
						 | 
					6a2b1669b3 | ||
| 
						 | 
					ca8264b3f4 | ||
| 
						 | 
					b44ecd8819 | ||
| 
						 | 
					987942959e | ||
| 
						 | 
					91992b48c1 | ||
| 
						 | 
					c9a335a6f9 | ||
| 
						 | 
					b7ed159b50 | ||
| 
						 | 
					c72961a52a | ||
| 
						 | 
					afe6afca36 | ||
| 
						 | 
					050acd239c | ||
| 
						 | 
					24505ee4f5 | ||
| 
						 | 
					63a249aba3 | ||
| 
						 | 
					7bd94df2a0 | ||
| 
						 | 
					761161a8e5 | ||
| 
						 | 
					513579a7ee | ||
| 
						 | 
					7165483d83 | ||
| 
						 | 
					f8bcf219cb | ||
| 
						 | 
					590506e306 | ||
| 
						 | 
					9a5439c580 | ||
| 
						 | 
					6b2f5fbb19 | ||
| 
						 | 
					051c147b41 | ||
| 
						 | 
					9111adf15f | ||
| 
						 | 
					ba18b27371 | ||
| 
						 | 
					2a287b2ae6 | ||
| 
						 | 
					fc9040f715 | ||
| 
						 | 
					0029022ef6 | ||
| 
						 | 
					94f728d3fd | ||
| 
						 | 
					d53ced7830 | ||
| 
						 | 
					053d8c44c2 | ||
| 
						 | 
					9f5767ea16 | ||
| 
						 | 
					e8d76b0555 | ||
| 
						 | 
					c2675600f6 | ||
| 
						 | 
					6f087b4ec1 | ||
| 
						 | 
					e94708606d | ||
| 
						 | 
					c248f1a762 | ||
| 
						 | 
					28402b0894 | ||
| 
						 | 
					7dd98e99f9 | ||
| 
						 | 
					dba195b396 | ||
| 
						 | 
					88b153bc12 | ||
| 
						 | 
					d4a47dc974 | ||
| 
						 | 
					fe3ea6edfd | ||
| 
						 | 
					6c8fc4846b | ||
| 
						 | 
					54d9656f09 | ||
| 
						 | 
					49da324c5d | ||
| 
						 | 
					9bf87697fd | ||
| 
						 | 
					f368f5a9c4 | ||
| 
						 | 
					eea85485e6 | ||
| 
						 | 
					1a544b3b82 | ||
| 
						 | 
					8b38fe9fe0 | ||
| 
						 | 
					1bf4addf63 | ||
| 
						 | 
					407e16e900 | ||
| 
						 | 
					6e9fe3248a | ||
| 
						 | 
					d8cf86fd6f | ||
| 
						 | 
					f8aa4a9588 | ||
| 
						 | 
					c249907846 | ||
| 
						 | 
					57c1524a9a | ||
| 
						 | 
					d8d82e2ba3 | ||
| 
						 | 
					807b512ef7 | ||
| 
						 | 
					b2f06b6777 | ||
| 
						 | 
					d7adff9a65 | ||
| 
						 | 
					b0d7e11d48 | ||
| 
						 | 
					fc9cdb61f2 | ||
| 
						 | 
					9c00492dc2 | ||
| 
						 | 
					1a6babd199 | ||
| 
						 | 
					1b693eed37 | ||
| 
						 | 
					afb566b6b4 | ||
| 
						 | 
					f870e9ed3e | ||
| 
						 | 
					4bcf13cb58 | ||
| 
						 | 
					946a6d6041 | ||
| 
						 | 
					372c213c2c | ||
| 
						 | 
					a5a79d3ab7 | ||
| 
						 | 
					e6c5cfb703 | ||
| 
						 | 
					7843eccae8 | ||
| 
						 | 
					7ca153abd0 | ||
| 
						 | 
					33b4774c49 | ||
| 
						 | 
					c243481432 | ||
| 
						 | 
					80873e4ea9 | ||
| 
						 | 
					4e4a1f11e6 | ||
| 
						 | 
					9bbe405cd0 | ||
| 
						 | 
					c440a4c730 | ||
| 
						 | 
					a1251371d7 | ||
| 
						 | 
					7d702e8332 | ||
| 
						 | 
					43d7c8d48c | ||
| 
						 | 
					7423583508 | ||
| 
						 | 
					08b0838f9a | ||
| 
						 | 
					038d821a7c | ||
| 
						 | 
					6a218814d3 | ||
| 
						 | 
					905f89b0f5 | ||
| 
						 | 
					14882bda78 | ||
| 
						 | 
					781fa4634b | ||
| 
						 | 
					cdb173fd6e | ||
| 
						 | 
					466cb4be89 | ||
| 
						 | 
					c39e2ffd56 | ||
| 
						 | 
					17bf09e276 | ||
| 
						 | 
					bc01f9f8fd | ||
| 
						 | 
					c0870c5694 | ||
| 
						 | 
					3b5174a2ea | ||
| 
						 | 
					af6885f3e8 | ||
| 
						 | 
					e01996095f | ||
| 
						 | 
					5d86f7b6ba | ||
| 
						 | 
					8d6ac6406d | ||
| 
						 | 
					40ff54f67e | ||
| 
						 | 
					cce7ac09d0 | ||
| 
						 | 
					73a18891c5 | ||
| 
						 | 
					fe22cedc1d | ||
| 
						 | 
					fa09c7c8b2 | ||
| 
						 | 
					15e28e3cc0 | ||
| 
						 | 
					2cb4f6b1fc | ||
| 
						 | 
					b17a483b85 | ||
| 
						 | 
					7c3e5443ab | ||
| 
						 | 
					f95a2851c8 | ||
| 
						 | 
					2b2eee352f | ||
| 
						 | 
					11569d8056 | ||
| 
						 | 
					bdf87452b6 | ||
| 
						 | 
					0c6bf81c24 | ||
| 
						 | 
					f2fa26fb07 | ||
| 
						 | 
					f5e212ff1e | ||
| 
						 | 
					461e6562ca | ||
| 
						 | 
					fd67d08402 | ||
| 
						 | 
					e6411d11b1 | ||
| 
						 | 
					dd81d947fc | ||
| 
						 | 
					23b887c30e | ||
| 
						 | 
					c4eae3f130 | ||
| 
						 | 
					41a04a2849 | ||
| 
						 | 
					ed1d34e678 | ||
| 
						 | 
					f44487338d | ||
| 
						 | 
					7aced85a31 | ||
| 
						 | 
					fbe0e2d6eb | ||
| 
						 | 
					6e34f0697c | ||
| 
						 | 
					a835f9f0cb | ||
| 
						 | 
					16715673c3 | ||
| 
						 | 
					c48c74f173 | ||
| 
						 | 
					f262348497 | ||
| 
						 | 
					7185bcd51f | ||
| 
						 | 
					28d05e2449 | ||
| 
						 | 
					7fafa21a1b | ||
| 
						 | 
					84f598e143 | ||
| 
						 | 
					e30f8628db | ||
| 
						 | 
					0be9c88106 | ||
| 
						 | 
					e046fc1ac5 | ||
| 
						 | 
					3a476ac493 | ||
| 
						 | 
					e33ec0cf50 | ||
| 
						 | 
					b4b70a988e | ||
| 
						 | 
					e66b381070 | ||
| 
						 | 
					771b598c09 | ||
| 
						 | 
					cd44f13171 | ||
| 
						 | 
					aa6b72ac87 | ||
| 
						 | 
					a467fe5ed7 | ||
| 
						 | 
					467411c6c3 | ||
| 
						 | 
					2648b7ca54 | ||
| 
						 | 
					de35c7024a | ||
| 
						 | 
					9d219c163d | ||
| 
						 | 
					f7434b5ec8 | ||
| 
						 | 
					5ed3360c0b | ||
| 
						 | 
					6f5974f875 | ||
| 
						 | 
					56db1da3cf | ||
| 
						 | 
					fef71f29c4 | ||
| 
						 | 
					d46b66878a | ||
| 
						 | 
					6cad80c4ad | ||
| 
						 | 
					68779caa2e | ||
| 
						 | 
					2a122ed283 | ||
| 
						 | 
					17c5fdf0d5 | ||
| 
						 | 
					f6274445a2 | ||
| 
						 | 
					3b0300b834 | ||
| 
						 | 
					4fbf1fe780 | ||
| 
						 | 
					dcf44fed58 | ||
| 
						 | 
					7136dc1c72 | ||
| 
						 | 
					0e4cedbc5e | ||
| 
						 | 
					dc139bcc30 | ||
| 
						 | 
					b204b183de | ||
| 
						 | 
					ab788bc1e3 | ||
| 
						 | 
					95b4c8d515 | ||
| 
						 | 
					b025644525 | ||
| 
						 | 
					9e87a60597 | ||
| 
						 | 
					5a70bea67a | ||
| 
						 | 
					2a95af3928 | ||
| 
						 | 
					0a0ca380d3 | ||
| 
						 | 
					4cfbf7f71c | ||
| 
						 | 
					de43148341 | ||
| 
						 | 
					0a2aab7d68 | ||
| 
						 | 
					745821c420 | ||
| 
						 | 
					57c4c754d0 | ||
| 
						 | 
					4b5c437533 | ||
| 
						 | 
					8d63b6a1ed | ||
| 
						 | 
					245a8adbf9 | ||
| 
						 | 
					4f7d98aace | ||
| 
						 | 
					b0c693cc3a | ||
| 
						 | 
					b2cca10e8b | ||
| 
						 | 
					a84b2ab5bb | ||
| 
						 | 
					4565342b05 | ||
| 
						 | 
					0ad54cc2d1 | ||
| 
						 | 
					865853da19 | ||
| 
						 | 
					392ed706fd | ||
| 
						 | 
					0ff0f25aaf | ||
| 
						 | 
					c157960846 | ||
| 
						 | 
					a5c00b5c81 | ||
| 
						 | 
					472bbdb59f | ||
| 
						 | 
					7877093713 | ||
| 
						 | 
					8cb2e51407 | ||
| 
						 | 
					d5cee81fb6 | ||
| 
						 | 
					bca020bc4d | ||
| 
						 | 
					5069f2844c | ||
| 
						 | 
					252df81f59 | ||
| 
						 | 
					7f89a4a26f | ||
| 
						 | 
					40f4167894 | ||
| 
						 | 
					0ef16989cd | ||
| 
						 | 
					3df3d6f516 | ||
| 
						 | 
					10395ef254 | ||
| 
						 | 
					83854c28db | ||
| 
						 | 
					fcbea2629c | ||
| 
						 | 
					26bc142cc2 | ||
| 
						 | 
					a4eb8e11c3 | ||
| 
						 | 
					9fd5d1db56 | ||
| 
						 | 
					1d05b4c981 | ||
| 
						 | 
					61f6535be8 | ||
| 
						 | 
					7dd329b5ee | ||
| 
						 | 
					b761904424 | ||
| 
						 | 
					36105412b1 | ||
| 
						 | 
					184b1b018c | ||
| 
						 | 
					f3e1b85d82 | ||
| 
						 | 
					626d012775 | ||
| 
						 | 
					9ad9c0ec6a | ||
| 
						 | 
					e13fed9fc6 | ||
| 
						 | 
					eb6d093e56 | ||
| 
						 | 
					af1ea610ea | ||
| 
						 | 
					d4d9190919 | ||
| 
						 | 
					4d3d1a02a8 | ||
| 
						 | 
					30c2aa96d6 | ||
| 
						 | 
					4edb1f80b0 | ||
| 
						 | 
					0a82459233 | ||
| 
						 | 
					db87b0dfa5 | ||
| 
						 | 
					cd42cf7583 | ||
| 
						 | 
					2d5980ff2a | ||
| 
						 | 
					d49c7a3adb | ||
| 
						 | 
					4fdd09a262 | ||
| 
						 | 
					d6878512c4 | ||
| 
						 | 
					8b1b8250ff | ||
| 
						 | 
					9dccbf747e | ||
| 
						 | 
					08727e1938 | ||
| 
						 | 
					7584820987 | ||
| 
						 | 
					d572356642 | ||
| 
						 | 
					3b5a2815a9 | ||
| 
						 | 
					f3cf01df25 | ||
| 
						 | 
					63e6e64ad3 | ||
| 
						 | 
					e8d7b48bff | ||
| 
						 | 
					12944d1ebd | ||
| 
						 | 
					fa1ff6e393 | ||
| 
						 | 
					98546b6e6a | ||
| 
						 | 
					2fef6fd1fa | ||
| 
						 | 
					20cf91f1dc | ||
| 
						 | 
					2efa78d590 | ||
| 
						 | 
					880af0671a | ||
| 
						 | 
					62471e4531 | ||
| 
						 | 
					b15f8535f8 | ||
| 
						 | 
					7a3a4493da | ||
| 
						 | 
					11078235c4 | ||
| 
						 | 
					f3e05cd08a | ||
| 
						 | 
					0ca3cabbe8 | ||
| 
						 | 
					44a75c1291 | ||
| 
						 | 
					4a4513a746 | ||
| 
						 | 
					60ff8660de | ||
| 
						 | 
					6fa0d671c0 | ||
| 
						 | 
					f478d7c9f0 | ||
| 
						 | 
					53e3e08d70 | ||
| 
						 | 
					c4d1ccb6f5 | ||
| 
						 | 
					e3520309fc | ||
| 
						 | 
					27bf72372e | ||
| 
						 | 
					ae4b1b17a9 | ||
| 
						 | 
					94cb03f4b5 | ||
| 
						 | 
					e691351976 | ||
| 
						 | 
					3190de873e | ||
| 
						 | 
					e8a637498d | ||
| 
						 | 
					e1195ac00a | ||
| 
						 | 
					6cd9ccc37c | ||
| 
						 | 
					25345302e8 | ||
| 
						 | 
					eccd5e9801 | ||
| 
						 | 
					ff355af9f2 | ||
| 
						 | 
					5967f4b0d4 | ||
| 
						 | 
					ff18618032 | ||
| 
						 | 
					20f03c356c | ||
| 
						 | 
					27fdc9e56e | ||
| 
						 | 
					52d9578a19 | ||
| 
						 | 
					f4c2938b41 | ||
| 
						 | 
					9f703de5ec | ||
| 
						 | 
					a327fd85e2 | ||
| 
						 | 
					9d22a86ec8 | ||
| 
						 | 
					29e0b194dd | ||
| 
						 | 
					ae9cf13fc2 | ||
| 
						 | 
					64ae67586a | ||
| 
						 | 
					838c7a5e89 | ||
| 
						 | 
					89bfc90f40 | ||
| 
						 | 
					acad9f57f9 | ||
| 
						 | 
					0d08dc410e | ||
| 
						 | 
					ebb3fb96cd | ||
| 
						 | 
					f31f23ff07 | ||
| 
						 | 
					d2aa3d1868 | ||
| 
						 | 
					c9e2fce94d | ||
| 
						 | 
					8b0e76dd55 | ||
| 
						 | 
					884618adfe | ||
| 
						 | 
					6e2e36e7a0 | ||
| 
						 | 
					9994df9601 | ||
| 
						 | 
					98f7271ac8 | ||
| 
						 | 
					087cd121b8 | ||
| 
						 | 
					2d52527fb4 | ||
| 
						 | 
					fe289e62b5 | ||
| 
						 | 
					2845475e3f | ||
| 
						 | 
					b307492487 | ||
| 
						 | 
					d48284f7ea | ||
| 
						 | 
					7e416797e9 | ||
| 
						 | 
					5d54ca7477 | ||
| 
						 | 
					b979b4e61a | ||
| 
						 | 
					2527f7984a | ||
| 
						 | 
					d9350b2362 | ||
| 
						 | 
					bd0b903f1a | ||
| 
						 | 
					f243c0df19 | ||
| 
						 | 
					7482978953 | ||
| 
						 | 
					77966689d4 | ||
| 
						 | 
					cf43939d65 | ||
| 
						 | 
					391ac4b351 | ||
| 
						 | 
					e1e48aadd9 | ||
| 
						 | 
					0681f206c4 | ||
| 
						 | 
					d257c6f3d3 | ||
| 
						 | 
					fa45c82cdc | ||
| 
						 | 
					e805b58da6 | ||
| 
						 | 
					943976d207 | ||
| 
						 | 
					3a2e5a6ccd | ||
| 
						 | 
					35ef036246 | ||
| 
						 | 
					e09c3bbdd3 | ||
| 
						 | 
					3b12076d4b | ||
| 
						 | 
					cfcf78ae28 | ||
| 
						 | 
					341ff9bf5c | ||
| 
						 | 
					10d8ca30b0 | ||
| 
						 | 
					4ebb5d099e | ||
| 
						 | 
					1e82b66bf0 | ||
| 
						 | 
					06a5e4273b | ||
| 
						 | 
					e123e7b0b0 | ||
| 
						 | 
					aeadc40c65 | ||
| 
						 | 
					7ef418ec52 | ||
| 
						 | 
					2ed52820b6 | ||
| 
						 | 
					e8fd7484b6 | ||
| 
						 | 
					ce5242cfe8 | ||
| 
						 | 
					af947879d8 | ||
| 
						 | 
					3ed112cde6 | ||
| 
						 | 
					99c6a9eccd | ||
| 
						 | 
					2029f6ea0a | ||
| 
						 | 
					e984e1f30f | ||
| 
						 | 
					f21260370f | ||
| 
						 | 
					fdae75c99b | ||
| 
						 | 
					a0489f2a0d | ||
| 
						 | 
					0123eacbdb | ||
| 
						 | 
					53401b6aa7 | ||
| 
						 | 
					9a5139f452 | ||
| 
						 | 
					2ee0c8c228 | ||
| 
						 | 
					c53562cc9c | ||
| 
						 | 
					ec5d7c2e5c | ||
| 
						 | 
					d6fc258485 | ||
| 
						 | 
					f953612695 | ||
| 
						 | 
					2ab93acca8 | ||
| 
						 | 
					326c6c496e | ||
| 
						 | 
					9f7f50664c | ||
| 
						 | 
					f6f1436123 | ||
| 
						 | 
					c3c519419d | ||
| 
						 | 
					e569a80b72 | ||
| 
						 | 
					284f437c1a | ||
| 
						 | 
					1fd44a9958 | ||
| 
						 | 
					cad34742f6 | ||
| 
						 | 
					323359b3c8 | ||
| 
						 | 
					35db8b45f0 | ||
| 
						 | 
					50ae815ceb | ||
| 
						 | 
					1a4389c90d | ||
| 
						 | 
					fdaa5ce1da | ||
| 
						 | 
					09d9936aed | ||
| 
						 | 
					b3f6109b1c | ||
| 
						 | 
					5fb3ffc240 | ||
| 
						 | 
					360db252bb | ||
| 
						 | 
					0b6e290271 | ||
| 
						 | 
					7f0174e6db | ||
| 
						 | 
					a25dad6c2e | ||
| 
						 | 
					6191a49ed3 | ||
| 
						 | 
					6252b075bc | ||
| 
						 | 
					7face138fd | ||
| 
						 | 
					382b83b093 | ||
| 
						 | 
					691687d1bc | ||
| 
						 | 
					5814b80a72 | ||
| 
						 | 
					e147fbb1fa | ||
| 
						 | 
					b9e256adfa | ||
| 
						 | 
					3b7bf04e22 | ||
| 
						 | 
					43408a724c | ||
| 
						 | 
					5fbd5bf9e2 | ||
| 
						 | 
					5e87828b29 | ||
| 
						 | 
					9066cedc29 | ||
| 
						 | 
					aa1cf0b228 | ||
| 
						 | 
					06a6a4408f | ||
| 
						 | 
					d5619d2b9d | ||
| 
						 | 
					2f6ac42efe | ||
| 
						 | 
					0bba3dd83d | ||
| 
						 | 
					abe60b62e6 | ||
| 
						 | 
					555b7df986 | ||
| 
						 | 
					b3786700e6 | ||
| 
						 | 
					ce9643d21b | ||
| 
						 | 
					4a5cb7f2f5 | ||
| 
						 | 
					42a7e902e6 | ||
| 
						 | 
					aebe080e85 | ||
| 
						 | 
					c316284924 | ||
| 
						 | 
					8d98b228ab | ||
| 
						 | 
					5b4c42ff05 | ||
| 
						 | 
					a596a4551a | ||
| 
						 | 
					0968f96982 | ||
| 
						 | 
					5931e13b9c | ||
| 
						 | 
					415c768ae4 | ||
| 
						 | 
					b4c8bf21d5 | ||
| 
						 | 
					5fe5db603d | ||
| 
						 | 
					9f7dd7f5d4 | ||
| 
						 | 
					e6d32aab7b | ||
| 
						 | 
					08bd6d963c | ||
| 
						 | 
					ff05fb14a6 | ||
| 
						 | 
					22d942b705 | ||
| 
						 | 
					0526372f28 | ||
| 
						 | 
					f99051906a | ||
| 
						 | 
					d1f7fd8bfd | ||
| 
						 | 
					fc1436a96d | ||
| 
						 | 
					d21568497b | ||
| 
						 | 
					df4beef060 | ||
| 
						 | 
					a52f195d41 | ||
| 
						 | 
					419019a656 | ||
| 
						 | 
					42b5635485 | ||
| 
						 | 
					a8fc5b01f3 | ||
| 
						 | 
					ead841d844 | ||
| 
						 | 
					67d7930aef | ||
| 
						 | 
					6f69995f4e | ||
| 
						 | 
					05252fa239 | ||
| 
						 | 
					1377439bb0 | ||
| 
						 | 
					407123a280 | ||
| 
						 | 
					750dd590c8 | ||
| 
						 | 
					44112a9d18 | ||
| 
						 | 
					b220bf0d99 | ||
| 
						 | 
					d0d93d7070 | ||
| 
						 | 
					4117961236 | ||
| 
						 | 
					68a3d71ee6 | ||
| 
						 | 
					bf5d741f0d | ||
| 
						 | 
					55a33bc408 | ||
| 
						 | 
					3ec35ed119 | ||
| 
						 | 
					3d8d6953ec | ||
| 
						 | 
					528db67c34 | ||
| 
						 | 
					b847e962aa | ||
| 
						 | 
					bbb9a3c63b | ||
| 
						 | 
					322cebc48c | ||
| 
						 | 
					1cceb3d880 | ||
| 
						 | 
					effc64db9a | ||
| 
						 | 
					7e5bd5f2c1 | ||
| 
						 | 
					e32cc4d1af | ||
| 
						 | 
					09a3cd850e | ||
| 
						 | 
					b0c876019a | ||
| 
						 | 
					6725f870d2 | ||
| 
						 | 
					0e5adc1f0a | ||
| 
						 | 
					57ebb93dc0 | ||
| 
						 | 
					05dc0bfa1d | ||
| 
						 | 
					2d0264116c | ||
| 
						 | 
					3938550ea8 | ||
| 
						 | 
					9f0c567794 | ||
| 
						 | 
					8672fcd2bb | ||
| 
						 | 
					3f2a92e801 | ||
| 
						 | 
					771e43583a | ||
| 
						 | 
					9353d5c1c4 | ||
| 
						 | 
					5e462f0f02 | ||
| 
						 | 
					a25f6fec9f | ||
| 
						 | 
					519edce0ed | ||
| 
						 | 
					0bc7702d95 | ||
| 
						 | 
					18be0d6d26 | ||
| 
						 | 
					1d4a435f20 | ||
| 
						 | 
					34e46fc6d3 | ||
| 
						 | 
					50956c51f7 | ||
| 
						 | 
					dd7bb28b6a | ||
| 
						 | 
					8516f41ba8 | ||
| 
						 | 
					15c3cc60f6 | ||
| 
						 | 
					716bca211b | ||
| 
						 | 
					8179813fe1 | ||
| 
						 | 
					d355de509b | ||
| 
						 | 
					b04a2d4c08 | ||
| 
						 | 
					6d3232a4f0 | ||
| 
						 | 
					2753075180 | ||
| 
						 | 
					d0166b25e4 | ||
| 
						 | 
					73ee657d74 | ||
| 
						 | 
					45913e5ee8 | ||
| 
						 | 
					e6369820a9 | ||
| 
						 | 
					22a5b339f7 | ||
| 
						 | 
					2cea3b6435 | ||
| 
						 | 
					5d2d06fb3e | ||
| 
						 | 
					0dd7bc7fb9 | ||
| 
						 | 
					0b0005337c | ||
| 
						 | 
					d26fb02bb9 | ||
| 
						 | 
					af683835d9 | ||
| 
						 | 
					c43647ca86 | ||
| 
						 | 
					6d02e70025 | ||
| 
						 | 
					5498c6f87d | ||
| 
						 | 
					11f59bc3ac | ||
| 
						 | 
					94cb7de79f | ||
| 
						 | 
					838f45775b | ||
| 
						 | 
					1c1422e4b5 | ||
| 
						 | 
					cd8ca6fc62 | ||
| 
						 | 
					b7a0a9d7c2 | ||
| 
						 | 
					7822ab113a | ||
| 
						 | 
					e250a91f09 | ||
| 
						 | 
					92a65dcda5 | ||
| 
						 | 
					4b129d94e4 | ||
| 
						 | 
					e7960d1d44 | ||
| 
						 | 
					95589307cd | ||
| 
						 | 
					20a0e4f3e0 | ||
| 
						 | 
					6a9213da64 | ||
| 
						 | 
					7a89e3cf33 | ||
| 
						 | 
					64607df929 | ||
| 
						 | 
					a62a1012fa | ||
| 
						 | 
					14efd0b2f9 | ||
| 
						 | 
					7ad2192df8 | ||
| 
						 | 
					3cb5cbd8d5 | ||
| 
						 | 
					cc9011cd68 | ||
| 
						 | 
					dc3d89008d | ||
| 
						 | 
					1893642187 | ||
| 
						 | 
					a9ece5772d | ||
| 
						 | 
					cf34716a57 | ||
| 
						 | 
					1337831061 | ||
| 
						 | 
					757e72100d | ||
| 
						 | 
					a75b819858 | ||
| 
						 | 
					da4a0f09ed | ||
| 
						 | 
					87d847a074 | ||
| 
						 | 
					84711beec0 | ||
| 
						 | 
					f3cf58c8ff | ||
| 
						 | 
					cf40497e6e | ||
| 
						 | 
					dfebc4b78d | ||
| 
						 | 
					15f41a2e7c | ||
| 
						 | 
					ad6e55ca17 | ||
| 
						 | 
					6b466d217a | ||
| 
						 | 
					00dcb304c7 | ||
| 
						 | 
					c6fb3d6f41 | ||
| 
						 | 
					ac3143811f | ||
| 
						 | 
					7e27dd7678 | ||
| 
						 | 
					c2508296a5 | ||
| 
						 | 
					a9b50ce6fc | ||
| 
						 | 
					eac98a6d4d | ||
| 
						 | 
					7e2b2a9a02 | ||
| 
						 | 
					353de471eb | ||
| 
						 | 
					85fc20b52d | ||
| 
						 | 
					cc25a781f8 | ||
| 
						 | 
					fc3012ba72 | ||
| 
						 | 
					d93a92c1c8 | ||
| 
						 | 
					f7f795f58a | ||
| 
						 | 
					2700f8cdd2 | ||
| 
						 | 
					6310de0d20 | ||
| 
						 | 
					218794be77 | ||
| 
						 | 
					af71ae649b | ||
| 
						 | 
					9bc72c1a06 | ||
| 
						 | 
					8d7c157751 | ||
| 
						 | 
					558a66fbe5 | ||
| 
						 | 
					f95b414d22 | ||
| 
						 | 
					e793a1e1aa | ||
| 
						 | 
					b76010cb5a | ||
| 
						 | 
					52475df783 | ||
| 
						 | 
					1f3f32d377 | ||
| 
						 | 
					3f5ba10354 | ||
| 
						 | 
					25f4a018d9 | ||
| 
						 | 
					a11a279c00 | ||
| 
						 | 
					fd4fdb31b5 | ||
| 
						 | 
					1921796d6d | ||
| 
						 | 
					543a2b9dc7 | ||
| 
						 | 
					dd23e03342 | ||
| 
						 | 
					5307c74f85 | ||
| 
						 | 
					d701c406e2 | ||
| 
						 | 
					3ba56a0a65 | ||
| 
						 | 
					4adafb6d1e | ||
| 
						 | 
					4453a51211 | ||
| 
						 | 
					14429d2943 | ||
| 
						 | 
					1a62a7831b | ||
| 
						 | 
					242e35c212 | ||
| 
						 | 
					ea763fdfd5 | ||
| 
						 | 
					e762b7ff48 | ||
| 
						 | 
					298068b2b9 | ||
| 
						 | 
					cb4120ec4b | ||
| 
						 | 
					5cfbb87bee | ||
| 
						 | 
					9e472ed83c | ||
| 
						 | 
					ebca753fc4 | ||
| 
						 | 
					548f45cd56 | ||
| 
						 | 
					8ffabf1813 | ||
| 
						 | 
					1f40d4f941 | ||
| 
						 | 
					41582045d0 | ||
| 
						 | 
					fd9e3fc03a | ||
| 
						 | 
					8c42b2bdb4 | ||
| 
						 | 
					7b1787fdbb | ||
| 
						 | 
					71fee0025d | ||
| 
						 | 
					1204cf1ba0 | ||
| 
						 | 
					7bd8d8c3ae | ||
| 
						 | 
					2c4d5fa38d | ||
| 
						 | 
					bedb2d943e | ||
| 
						 | 
					a3640bd9bf | ||
| 
						 | 
					7c0b9ffe06 | ||
| 
						 | 
					161c7d30ca | ||
| 
						 | 
					4ff6e792cd | ||
| 
						 | 
					af5df890a5 | ||
| 
						 | 
					9ba011003a | ||
| 
						 | 
					bb168d35a8 | ||
| 
						 | 
					3306d30094 | ||
| 
						 | 
					6516e0dfd2 | ||
| 
						 | 
					00a396014b | ||
| 
						 | 
					13356047dc | ||
| 
						 | 
					8a6488b067 | ||
| 
						 | 
					1c2ea56f42 | ||
| 
						 | 
					1d7ae300e2 | ||
| 
						 | 
					6013e186ed | ||
| 
						 | 
					207d3d3340 | ||
| 
						 | 
					5a6cde1446 | ||
| 
						 | 
					63f7d826bc | ||
| 
						 | 
					ff8773f6bd | ||
| 
						 | 
					a868cb97d9 | ||
| 
						 | 
					915d73e6f2 | ||
| 
						 | 
					5f4f6e37b5 | ||
| 
						 | 
					9c350311e8 | ||
| 
						 | 
					3c6ba72a2a | ||
| 
						 | 
					816442f5f0 | ||
| 
						 | 
					3b51d18ce7 | ||
| 
						 | 
					6696b6661a | ||
| 
						 | 
					8c87478636 | ||
| 
						 | 
					d870b072d7 | ||
| 
						 | 
					2ea2af7d2a | ||
| 
						 | 
					f737ea96f3 | ||
| 
						 | 
					05f90394db | ||
| 
						 | 
					c24b0c6bb4 | ||
| 
						 | 
					e07a4dc7ba | ||
| 
						 | 
					fc6748a46b | ||
| 
						 | 
					7697c46652 | ||
| 
						 | 
					ed52e5afd1 | ||
| 
						 | 
					33a5b84181 | ||
| 
						 | 
					c09a407f4c | ||
| 
						 | 
					d35784ec61 | ||
| 
						 | 
					53e012f296 | ||
| 
						 | 
					2a9d0a5e7d | ||
| 
						 | 
					474f4572f2 | ||
| 
						 | 
					bf57cb209f | ||
| 
						 | 
					9bc41c1709 | ||
| 
						 | 
					fe10b8650f | ||
| 
						 | 
					3a311c9584 | ||
| 
						 | 
					d1106f53e0 | ||
| 
						 | 
					a3a1bba5ef | ||
| 
						 | 
					028d66befc | ||
| 
						 | 
					bb59cd5742 | ||
| 
						 | 
					604e3068b2 | ||
| 
						 | 
					27f1d3b704 | ||
| 
						 | 
					d007623347 | ||
| 
						 | 
					6a5cf7a1fa | ||
| 
						 | 
					3adfe249b0 | ||
| 
						 | 
					923893e160 | ||
| 
						 | 
					256e5360d4 | ||
| 
						 | 
					304c597a2f | ||
| 
						 | 
					f86d3a69d2 | ||
| 
						 | 
					d7c8adfd82 | ||
| 
						 | 
					55cd069043 | ||
| 
						 | 
					3ca0e9c420 | ||
| 
						 | 
					1dd4323613 | ||
| 
						 | 
					d78916f85f | ||
| 
						 | 
					1840d15397 | ||
| 
						 | 
					b98d1216b1 | ||
| 
						 | 
					27db727321 | ||
| 
						 | 
					3f6b1f6ccb | ||
| 
						 | 
					6d633b372a | ||
| 
						 | 
					91352e855a | ||
| 
						 | 
					8bb9b594cf | ||
| 
						 | 
					6d2fd2e641 | ||
| 
						 | 
					422fbcb0b7 | ||
| 
						 | 
					afce106186 | ||
| 
						 | 
					f21c8154ed | ||
| 
						 | 
					3988a648d6 | ||
| 
						 | 
					1b632894d3 | ||
| 
						 | 
					5e128f89f6 | ||
| 
						 | 
					fff0b15ae5 | ||
| 
						 | 
					a7e14f1093 | ||
| 
						 | 
					94eeaeb8d3 | ||
| 
						 | 
					64191e8303 | ||
| 
						 | 
					21cfb71617 | ||
| 
						 | 
					806457063f | ||
| 
						 | 
					b9213b73bd | ||
| 
						 | 
					d7f0102aa2 | ||
| 
						 | 
					f09e61a59a | ||
| 
						 | 
					d426aaa88a | ||
| 
						 | 
					19e45389e1 | ||
| 
						 | 
					6d2389945b | ||
| 
						 | 
					14c48253f6 | ||
| 
						 | 
					e5ff25b92d | ||
| 
						 | 
					5c88888e02 | ||
| 
						 | 
					10057de9b3 | ||
| 
						 | 
					cc88ebd2b9 | ||
| 
						 | 
					6baedf909d | ||
| 
						 | 
					f39d9d6f1b | ||
| 
						 | 
					ab61a95f83 | ||
| 
						 | 
					10ceed30c6 | ||
| 
						 | 
					2b9aa94f3a | ||
| 
						 | 
					848fb975ed | ||
| 
						 | 
					d7f59dac84 | ||
| 
						 | 
					dd47e615ee | ||
| 
						 | 
					8f2f7ea1a5 | ||
| 
						 | 
					80a8efd8ce | ||
| 
						 | 
					d9dce77ef4 | ||
| 
						 | 
					ce7053a1fe | ||
| 
						 | 
					0db1530171 | ||
| 
						 | 
					3745504107 | ||
| 
						 | 
					57533fd831 | ||
| 
						 | 
					f57a0d4d6b | ||
| 
						 | 
					22772ca33e | ||
| 
						 | 
					dba6ff1d51 | ||
| 
						 | 
					40146dedaf | ||
| 
						 | 
					387b822f53 | ||
| 
						 | 
					b9a3563e5b | ||
| 
						 | 
					3d6468326a | ||
| 
						 | 
					298e37ec53 | ||
| 
						 | 
					5b137c457b | ||
| 
						 | 
					5218a3fbac | ||
| 
						 | 
					4569cb432d | ||
| 
						 | 
					611e598756 | ||
| 
						 | 
					937d79d28f | ||
| 
						 | 
					23c2a771d3 | ||
| 
						 | 
					58a890e836 | ||
| 
						 | 
					6a869e120c | ||
| 
						 | 
					ae7c298b1a | ||
| 
						 | 
					53bfe12ac1 | ||
| 
						 | 
					140ea683a6 | ||
| 
						 | 
					0634a97598 | ||
| 
						 | 
					3479c794de | ||
| 
						 | 
					89cad116f7 | ||
| 
						 | 
					19c84eb694 | ||
| 
						 | 
					eae390acf5 | ||
| 
						 | 
					10567afbb9 | ||
| 
						 | 
					51bad3bf3c | ||
| 
						 | 
					9134d8841d | ||
| 
						 | 
					e9a026c131 | ||
| 
						 | 
					9a2fd0e2b2 | ||
| 
						 | 
					522f7e6844 | ||
| 
						 | 
					cb4f46decc | ||
| 
						 | 
					81256279a8 | ||
| 
						 | 
					039bd1ddc0 | ||
| 
						 | 
					0791d4797f | ||
| 
						 | 
					6a06142e1e | ||
| 
						 | 
					ef53dca062 | ||
| 
						 | 
					6ce761edda | ||
| 
						 | 
					d8fd218409 | ||
| 
						 | 
					edc2310599 | ||
| 
						 | 
					b1cd13d629 | ||
| 
						 | 
					b81940351f | ||
| 
						 | 
					a42e99c4aa | ||
| 
						 | 
					ff40b521b7 | ||
| 
						 | 
					85392496e7 | ||
| 
						 | 
					29cae9975e | ||
| 
						 | 
					170d6b28f8 | ||
| 
						 | 
					9a8b404054 | ||
| 
						 | 
					41af5187aa | ||
| 
						 | 
					a844ca161f | ||
| 
						 | 
					e09efba313 | ||
| 
						 | 
					96a0dbea2d | ||
| 
						 | 
					5b3b5271ad | ||
| 
						 | 
					d7d13c12fe | ||
| 
						 | 
					54220d0e71 | ||
| 
						 | 
					4a2e3586f1 | ||
| 
						 | 
					f808e85da9 | ||
| 
						 | 
					1671d1f580 | ||
| 
						 | 
					7de1bf9d95 | ||
| 
						 | 
					7368b0cefb | ||
| 
						 | 
					4af43d676a | ||
| 
						 | 
					67dc848b2d | ||
| 
						 | 
					7ec8f0d26b | ||
| 
						 | 
					eaf08a9971 | ||
| 
						 | 
					5bdb9e972e | ||
| 
						 | 
					d4d87054c4 | ||
| 
						 | 
					0f93929544 | ||
| 
						 | 
					1c0e794f87 | ||
| 
						 | 
					f9bce5a5f9 | ||
| 
						 | 
					797ae096c8 | ||
| 
						 | 
					6d76918424 | ||
| 
						 | 
					2aced893c6 | ||
| 
						 | 
					f0373cd789 | ||
| 
						 | 
					2f88dc64fc | ||
| 
						 | 
					781ca77794 | ||
| 
						 | 
					c6e453fb00 | ||
| 
						 | 
					a40b3dd377 | ||
| 
						 | 
					096b3534d8 | ||
| 
						 | 
					5324244c55 | ||
| 
						 | 
					993f1dc853 | ||
| 
						 | 
					d8a4e9e1ab | ||
| 
						 | 
					b3ffd33507 | ||
| 
						 | 
					c93870316c | ||
| 
						 | 
					fc9906624e | ||
| 
						 | 
					ba6209ba54 | ||
| 
						 | 
					f9769a73fe | ||
| 
						 | 
					3a2f56cb95 | ||
| 
						 | 
					a4d33879dc | ||
| 
						 | 
					e2a91d1ea9 | ||
| 
						 | 
					f30f80d117 | ||
| 
						 | 
					266274135e | ||
| 
						 | 
					a10439b67c | ||
| 
						 | 
					0fd8d0e2bf | ||
| 
						 | 
					47e2707fd3 | ||
| 
						 | 
					f7bb4a7d60 | ||
| 
						 | 
					6102a31a31 | ||
| 
						 | 
					8dcc114873 | ||
| 
						 | 
					8cc9aeba4a | ||
| 
						 | 
					ba0823c38c | 
							
								
								
									
										34
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
<!--
 | 
			
		||||
## Before you hit that Submit button....
 | 
			
		||||
 | 
			
		||||
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
 | 
			
		||||
 | 
			
		||||
If your issue is:
 | 
			
		||||
  - a general 'how-to' type question,
 | 
			
		||||
  - a feature request or suggestion for a change,
 | 
			
		||||
  - or problems with 3rd party (`node-red-contrib-`) nodes
 | 
			
		||||
 | 
			
		||||
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
 | 
			
		||||
 | 
			
		||||
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
 | 
			
		||||
 | 
			
		||||
That way the whole Node-RED user community can help, rather than rely on the core development team.
 | 
			
		||||
 | 
			
		||||
## So you have a real issue to raise...
 | 
			
		||||
 | 
			
		||||
To help us understand the issue, please fill-in as much of the following information as you can:
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
### What are the steps to reproduce?
 | 
			
		||||
 | 
			
		||||
### What happens?
 | 
			
		||||
 | 
			
		||||
### What do you expect to happen?
 | 
			
		||||
 | 
			
		||||
### Please tell us about your environment:
 | 
			
		||||
 | 
			
		||||
- [ ] Node-RED version:
 | 
			
		||||
- [ ] node.js version:
 | 
			
		||||
- [ ] npm version:
 | 
			
		||||
- [ ] Platform/OS:
 | 
			
		||||
- [ ] Browser:
 | 
			
		||||
							
								
								
									
										34
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
<!--
 | 
			
		||||
## Before you hit that Submit button....
 | 
			
		||||
 | 
			
		||||
Please read our [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
 | 
			
		||||
before submitting a pull-request.
 | 
			
		||||
 | 
			
		||||
## Types of changes
 | 
			
		||||
 | 
			
		||||
What types of changes does your code introduce?
 | 
			
		||||
Put an `x` in the boxes that apply
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
- [ ] Bugfix (non-breaking change which fixes an issue)
 | 
			
		||||
- [ ] New feature (non-breaking change which adds functionality)
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
If you want to raise a pull-request with a new feature, or a refactoring
 | 
			
		||||
of existing code, it **may well get rejected** if it hasn't been discussed on
 | 
			
		||||
the [forum](https://discourse.nodered.org) or
 | 
			
		||||
[slack team](https://nodered.org/slack) first.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
## Proposed changes
 | 
			
		||||
 | 
			
		||||
<!-- Describe the nature of this change. What problem does it address? -->
 | 
			
		||||
 | 
			
		||||
## Checklist
 | 
			
		||||
<!-- Put an `x` in the boxes that apply -->
 | 
			
		||||
 | 
			
		||||
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
 | 
			
		||||
- [ ] For non-bugfix PRs, I have discussed this change on the mailing list/slack team.
 | 
			
		||||
- [ ] I have run `grunt` to verify the unit tests pass
 | 
			
		||||
- [ ] I have added suitable unit tests to cover the new/changed functionality
 | 
			
		||||
							
								
								
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,21 +1,12 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
language: node_js
 | 
			
		||||
env:
 | 
			
		||||
  - CXX="g++-4.8"
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    sources:
 | 
			
		||||
    - ubuntu-toolchain-r-test
 | 
			
		||||
    packages:
 | 
			
		||||
    - g++-4.8
 | 
			
		||||
    - gcc-4.8
 | 
			
		||||
node_js:
 | 
			
		||||
  - "8"
 | 
			
		||||
  - "7"
 | 
			
		||||
  - "6"
 | 
			
		||||
  - "4"
 | 
			
		||||
script:
 | 
			
		||||
  - istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage
 | 
			
		||||
before_script:
 | 
			
		||||
  - npm install -g istanbul
 | 
			
		||||
  - npm install coveralls
 | 
			
		||||
matrix:
 | 
			
		||||
  include:
 | 
			
		||||
    - node_js: "10"
 | 
			
		||||
      script:
 | 
			
		||||
        - ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
 | 
			
		||||
      before_script:
 | 
			
		||||
        - npm install -g istanbul coveralls
 | 
			
		||||
    - node_js: "8"
 | 
			
		||||
    - node_js: "6"
 | 
			
		||||
    - node_js: "4"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										410
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										410
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,377 @@
 | 
			
		||||
#### 0.19.2: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Ensure node default colour is used if palette.theme has no match
 | 
			
		||||
 - fix lost messages / properties in TCPRequest Node; closes #1863 (#1864)
 | 
			
		||||
 - Fix typo in template.html
 | 
			
		||||
 - Improve error reporting from context plugin loading
 | 
			
		||||
 - Prevent no-op edit of node marking as changed due to icon
 | 
			
		||||
 - Change node must handle empty rule set
 | 
			
		||||
 | 
			
		||||
#### 0.19.1: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Pull in latest twitter node
 | 
			
		||||
 - Handle windows paths for context storage
 | 
			
		||||
 - Handle persisting objects with circular refs in context
 | 
			
		||||
 - Ensure js editor can expand to fill available space
 | 
			
		||||
 - Add example localfilesystem contextStorage to settings
 | 
			
		||||
 - Fix template node handling of nested context tags
 | 
			
		||||
 | 
			
		||||
#### 0.19: Milestone Release
 | 
			
		||||
 | 
			
		||||
Editor
 | 
			
		||||
 | 
			
		||||
 - Add editorTheme.palette.theme to allow overriding colours
 | 
			
		||||
 - Index all node properties when searching Fixes #1446
 | 
			
		||||
 - Handle NaN and Infinity properly in debug sidebar Fixes #1778 #1779
 | 
			
		||||
 - Prevent horizontal scroll when palette name cannot wrap
 | 
			
		||||
 - Ignore middle-click on node/ports to enable panning
 | 
			
		||||
 - Better wire layout when looping back
 | 
			
		||||
 - fix appearence of retry button of remote branch management dialog
 | 
			
		||||
 - Handle releasing ctrl when using quick-add node dialog
 | 
			
		||||
 - Add $env function to JSONata expressions
 | 
			
		||||
 - Widen support for env var to use ${} or $() syntax
 | 
			
		||||
 - Add env-var support to TypedInput
 | 
			
		||||
 - Show unknown node properties in info tab
 | 
			
		||||
 - Add node icon picker widget
 | 
			
		||||
 - Only edit nodes on dbl click on primary button with no modifiers
 | 
			
		||||
 - Allow subflows to be put in any palette category
 | 
			
		||||
 - Add flow navigator widget
 | 
			
		||||
 - Cache flow library result to improve response time Fixes #1753
 | 
			
		||||
 - Add middle-button-drag to pan the workspace
 | 
			
		||||
 - allow multi-line category name in editor
 | 
			
		||||
 - Redesign sidebar tabs
 | 
			
		||||
 - Do not disable the export-clipboard menu option with empty selection
 | 
			
		||||
 | 
			
		||||
Nodes
 | 
			
		||||
 | 
			
		||||
 - Change: Ensure runtime errors in Change node can be caught Fixes #1769
 | 
			
		||||
 - File: Add output to File Out node
 | 
			
		||||
 - Function: add expandable JavaScript editor pane
 | 
			
		||||
 - Function: allow id and name reference in function node code (#1731)
 | 
			
		||||
 - HTTP Request: Move to request module
 | 
			
		||||
 - HTTP: Ensure apiMaxLength applies to HTTP Nodes Fixes #1278
 | 
			
		||||
 - Join: accumulate top level properties
 | 
			
		||||
 - Join: allow environment variable as reduce init value
 | 
			
		||||
 - JSON: add JSON schema validation via msg.schema
 | 
			
		||||
 - Pi: Let nrgpio code work with python 3
 | 
			
		||||
 - Pi: let Pi nodes be visible/editable on all platforms
 | 
			
		||||
 - Switch: add isEmpty rule
 | 
			
		||||
 - TCP: queue messages while connecting; closes #1414
 | 
			
		||||
 - TLS: Add servername option to TLS config node for SNI Fixes #1805
 | 
			
		||||
 - UDP: Don't accidentally re-use udp port when set to not do so
 | 
			
		||||
 | 
			
		||||
Persistent Context
 | 
			
		||||
 | 
			
		||||
 - Add Context data sidebar
 | 
			
		||||
 - Add persistable context option
 | 
			
		||||
 - Add default memory store
 | 
			
		||||
 - Add file-based context store
 | 
			
		||||
 - Add async mode to evaluateJSONataExpression
 | 
			
		||||
 - Update RED.util.evaluateNodeProperty to support context stores
 | 
			
		||||
 | 
			
		||||
Runtime
 | 
			
		||||
 | 
			
		||||
 - Support flow.disabled and .info in /flow API
 | 
			
		||||
 - Node errors should be Strings not Errors Fixes #1781
 | 
			
		||||
 - Add detection of connection timeout in git communication Fixes #1770
 | 
			
		||||
 - Handle loading empty nodesDir
 | 
			
		||||
 - Add 'private' property to userDir generated package.json
 | 
			
		||||
 - Add RED.require to allow nodes to access other modules
 | 
			
		||||
 - Ensure add/remove modules are run sequentially
 | 
			
		||||
 | 
			
		||||
#### 0.18.7: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Editor Fixes
 | 
			
		||||
 | 
			
		||||
 - Do not trim wires if node declares outputs in defaults but misses value Fixes #1737
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - Relax twitter node version ready for major version bump
 | 
			
		||||
 - Pass Date into the Function node sandbox to fix instanceof tests
 | 
			
		||||
 - let TCP in node report remote ip and port when in single packet mode
 | 
			
		||||
 - typo fix in node help (#1735)
 | 
			
		||||
 | 
			
		||||
Other Fixes
 | 
			
		||||
 - Tidy up default grunt task and fixup test break due to reorder Fixes #1738
 | 
			
		||||
 - Bump jsonata version
 | 
			
		||||
 | 
			
		||||
#### 0.18.6: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Editor Fixes
 | 
			
		||||
 | 
			
		||||
 - Handle a node having wires in the editor on ports it no longer has Fixes #1724
 | 
			
		||||
 - Add missing ACE snippet files
 | 
			
		||||
 - Fix wireClippedNodes is not defined Fixes #1726
 | 
			
		||||
 - Split node html to isolate bad nodes when loading
 | 
			
		||||
 - Avoid unnecessary use of .html() where .text() will do
 | 
			
		||||
 | 
			
		||||
 - Add editorTheme.projects.enabled to default settings.js"
 | 
			
		||||
 | 
			
		||||
#### 0.18.5: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Projects
 | 
			
		||||
 | 
			
		||||
 - Add clone project to welcome screen
 | 
			
		||||
 - Handle cloning a project without package.json
 | 
			
		||||
 - Keep remote branch state in sync between editor and runtime
 | 
			
		||||
 | 
			
		||||
New Features
 | 
			
		||||
 | 
			
		||||
 - Add type checks to switch node options (#1714)
 | 
			
		||||
 - add output property select to HTML parse node (#1701)
 | 
			
		||||
 - Add Prevent Following Redirect to HTTP Request node (#615) (#1684)
 | 
			
		||||
 - Add debug and trace functions to function node (#1654)
 | 
			
		||||
 - Enable user defined icon for subflow
 | 
			
		||||
 - Add MQTT disconnect message and rework broker node UI (#1719)
 | 
			
		||||
 - Japanese message catalogue updates (#1723)
 | 
			
		||||
 - Show node load errors in the Palette Manager view
 | 
			
		||||
 | 
			
		||||
Editor Fixes
 | 
			
		||||
 | 
			
		||||
 - Highlight subflow node when log msg comes from inside Fixes #1698
 | 
			
		||||
 - Ensure node wires array is not longer than outputs value Fixes #1678
 | 
			
		||||
 - Allow importing an unknown config node to be undone Fixes #1681
 | 
			
		||||
 - Ensure keyboard shortcuts get saved in runtime settings Fixes #1696
 | 
			
		||||
 - Don't mark a subflow changed when actually modified nothing (#1665)
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - bind to correct port when doing udp broadcast/multicast (#1686)
 | 
			
		||||
 - Provide full error stack in Function node log message (#1700)
 | 
			
		||||
 - Fix http request doc type Fixes #1690
 | 
			
		||||
 - Make debug slightly larger to pass WCAG AA rating
 | 
			
		||||
 - Make core nodes labels more consistent, to close #1673
 | 
			
		||||
 - Allow template node to be updated more than once Fixes #1671
 | 
			
		||||
 - Fix the problem that output labels of switch node sometimes disappear (#1664)
 | 
			
		||||
 - Chinese translations for core nodes (#1607)
 | 
			
		||||
 | 
			
		||||
Runtime Fixes
 | 
			
		||||
 | 
			
		||||
 - Handle and display for invalid flow credentials when project is disabled #1689 (#1694)
 | 
			
		||||
 - node-red-pi: fix behavior with old bash version (#1713)
 | 
			
		||||
 - Fix ENOENT error on first start when no user dir (#1711)
 | 
			
		||||
 - Handle null error object in Flow.handleError Fixes #1721
 | 
			
		||||
 - update settings comments to describe how to setup for ipv6 (#1675)
 | 
			
		||||
 - Remove credential props after diffing flow to prevent future false positives Fixes #1359
 | 
			
		||||
 - Log error if settings unavailable when saving user settings Fixes #1645
 | 
			
		||||
 - Keep backup of .config.json
 | 
			
		||||
 - Add warning if using \_credentialSecret from .config.json
 | 
			
		||||
 - Filter req.user in /settings to prevent potentially leaking info
 | 
			
		||||
 | 
			
		||||
#### 0.18.4: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Projects
 | 
			
		||||
 | 
			
		||||
 - Ensure sshkey file path is properly escaped on Windows
 | 
			
		||||
 - Normalize ssh key paths for Windows file names
 | 
			
		||||
 - Ensure userDir is an absolute path when used with sshkeygen
 | 
			
		||||
 - Detect if there are no existing flows to migrate into a project
 | 
			
		||||
 - Use relative urls when retriving flow history
 | 
			
		||||
 - Add credentialSecret to clone pane
 | 
			
		||||
 - Delay clearing inflight when changing credentials key
 | 
			
		||||
 - Mark deploy inflight when reverting a file change
 | 
			
		||||
 - Handle missing_flow_file error on clone properly
 | 
			
		||||
 - Remote project from cached list on delete so it can be reused
 | 
			
		||||
 - Fix tests for existing file flag in settings
 | 
			
		||||
 | 
			
		||||
Editor Fixes
 | 
			
		||||
 | 
			
		||||
 - Fix merging a remote diff
 | 
			
		||||
 - Fixed the problems when using a node without defaults
 | 
			
		||||
 - Disable user defined icon for subflow
 | 
			
		||||
 - getDefaultNodeIcon should handle subflow instance nodes Fixes #1635
 | 
			
		||||
 - Add Japanese info text for core nodes
 | 
			
		||||
 - Fix message lookup for core nodes in case of i18 locales directory exists
 | 
			
		||||
 - Prevent the last tab from being deleted
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - Ensure trigger gets reset when 2nd output is null
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 0.18.3: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Projects
 | 
			
		||||
 | 
			
		||||
 - Fix permissions on git/ssh scripts
 | 
			
		||||
 - Add support for GIT_SSH on older levels of git
 | 
			
		||||
 - Handle host key verification as auth error
 | 
			
		||||
 - Ensure commit list has a refs object even if empty
 | 
			
		||||
 - Make git error detection case-insensitive
 | 
			
		||||
 - Fix up merge conflict handling
 | 
			
		||||
 - Use flow-diff when looking at flow file changes
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - Ensure debug tools show for 'complete msg object'
 | 
			
		||||
 - Fix msg.parts handling in concat mode of Batch node
 | 
			
		||||
 | 
			
		||||
Editor Fixes
 | 
			
		||||
 | 
			
		||||
 - Fix offset calculation when dragging node from palette
 | 
			
		||||
 - Allow a library entry to use non-default node-input- prefixes
 | 
			
		||||
 - Change remote-diff shortcut and add it to keymap Fixes #1628
 | 
			
		||||
 | 
			
		||||
#### 0.18.2: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Projects
 | 
			
		||||
 | 
			
		||||
 - Filter out %D from git log command for older git versions
 | 
			
		||||
 - Ensure projects are created as logged in user
 | 
			
		||||
 - Better error handling/reporting in project creation
 | 
			
		||||
 - Add Project Settings menu option
 | 
			
		||||
 - Refresh vc sidebar on remote add/remove
 | 
			
		||||
 - Fix auth prompt for ssh repos
 | 
			
		||||
 - Prevent http git urls from including username/pword
 | 
			
		||||
 - Fix fetch auth handling on non-default remote
 | 
			
		||||
 - Avoid exception if git not installed
 | 
			
		||||
 - Check version of git client on startup
 | 
			
		||||
 - Fix pull/push when no tracked branch
 | 
			
		||||
 - Add git_pull_unrelated_history handling
 | 
			
		||||
 - Handle delete of last remote in project settings
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - Fix and Add some Chinese translations
 | 
			
		||||
 - Update sort/batch docs
 | 
			
		||||
 - Don't assume node has defaults when exporting icon property
 | 
			
		||||
 - Ensure send is last thing trigger does
 | 
			
		||||
 - Ensure trigger doesn't set two simultaneous timeouts
 | 
			
		||||
 - Add missing property select var to HTML node
 | 
			
		||||
 - Add a default keepalive to tcp client mode
 | 
			
		||||
 - Move node.send in exec and httprequest nodes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 0.18.1: Maintenance Release
 | 
			
		||||
 | 
			
		||||
Projects
 | 
			
		||||
 | 
			
		||||
 - Handle more repo clone error cases
 | 
			
		||||
 - Relax validation of git urls
 | 
			
		||||
 - Revalidate project name on return to project-details view
 | 
			
		||||
 - Avoid unnecessary project refresh on branch-switch Fixes #1597
 | 
			
		||||
 - Add support for file:// git urls
 | 
			
		||||
 - Handle project first-run without existing flow file
 | 
			
		||||
 - Handle delete of last remote in project settings
 | 
			
		||||
 - Add git_pull_unrelated_history handling
 | 
			
		||||
 - Fix pull/push when no tracked branch
 | 
			
		||||
 - Remember to disable projects in editor when git not found
 | 
			
		||||
 | 
			
		||||
Node Fixes
 | 
			
		||||
 | 
			
		||||
 - Trigger node migration - ensure bytopic not blank
 | 
			
		||||
 - Add HEAD to list of methods with no body in http req node #1598
 | 
			
		||||
 - Do not include payload in GET requests Fixes #1598
 | 
			
		||||
 - Update sort/batch docs Fixes #1601
 | 
			
		||||
 - Don't assume node has defaults when exporting icon property
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 0.18: Milestone Release
 | 
			
		||||
 | 
			
		||||
Runtime
 | 
			
		||||
 | 
			
		||||
 - Beta: Projects - must be enabled in settings file
 | 
			
		||||
 - Allow port zero for Express (#1363)
 | 
			
		||||
 - Better error reporting when module provides duplicate type
 | 
			
		||||
 - Update jsonata to 1.5.0
 | 
			
		||||
 - add express-session memorystore without leaks (#1435)
 | 
			
		||||
 - Allow adminAuth.user to be a Function Fixes #1461
 | 
			
		||||
 - Ensure RED.server is set even if admin api disabled
 | 
			
		||||
 - Ensure strategy login button uses relative URL Fixes #1481
 | 
			
		||||
 - ignore `_msgid` when merging full objects
 | 
			
		||||
 - Move node install to spawn to allow for big stdout Fixes #1488
 | 
			
		||||
 - SIGINT handler should wait for stop to complete before exit
 | 
			
		||||
 | 
			
		||||
Editor
 | 
			
		||||
 | 
			
		||||
 - allow a node's icon to be set dynamically (#1490)
 | 
			
		||||
 - Batch messages sent over comms to increase throughput
 | 
			
		||||
 - Migrate deploy confirmations to notifications
 | 
			
		||||
 - `oneditdelete` should be available to all node types Closes #1346
 | 
			
		||||
 - Sort typeSearch results based on position of match
 | 
			
		||||
 - Update ACE to test and add python highlighter (#1373)
 | 
			
		||||
 - Clear mouse state when typeSearch cancelled Fixes #1517
 | 
			
		||||
 - Handle scoped modules via palette editor
 | 
			
		||||
 - TypedInput: handle user defined value/labels options Fixes #1549
 | 
			
		||||
 | 
			
		||||
Nodes
 | 
			
		||||
 | 
			
		||||
 - add msg. select to range and yaml nodes
 | 
			
		||||
 - add property choice to xml, sentiment nodes
 | 
			
		||||
 - mqtt: Add 'name' to mqtt-broker node, and label it by this if it is set. (#1364)
 | 
			
		||||
 - Add option to JSON node to ensure particular encoding
 | 
			
		||||
 - add parts support for HTML node (#1495)
 | 
			
		||||
 - Add passphrase to TLS node
 | 
			
		||||
 - Add rc property to exec node outputs 1 and 2 (#1401)
 | 
			
		||||
 - Add skip first n lines capability to csv node (#1535)
 | 
			
		||||
 - Add support for rejectUnauthorized msg property
 | 
			
		||||
 - Add TLS options to WebSocket client
 | 
			
		||||
 - Added parsed YAML support for template node (#1443)
 | 
			
		||||
 - Allow delay node in rate-limit mode to be reset Fixes #1360
 | 
			
		||||
 - Allow setTimeout in Function node to be promisified in node 8
 | 
			
		||||
 - Debug to status option (#1499)
 | 
			
		||||
 - enable template config via msg.template for stored or generated templates (#1503)
 | 
			
		||||
 - HTTP REQUEST: Adding PROPPATCH and PROPFIND http methods (#1531)
 | 
			
		||||
 - Initial support of merge & reduce mode for JOIN node (#1546)
 | 
			
		||||
 - Initial support of new BATCH node (#1548)
 | 
			
		||||
 - Initial support of sequence rules for SWITCH node (#1545)
 | 
			
		||||
 - initial support of SORT node (#1500)
 | 
			
		||||
 - Inject node - let once delay be editable (#1541)
 | 
			
		||||
 - Introduce `nodeMaxMessageBufferLength` setting for msg sequence nodes
 | 
			
		||||
 - Let CSV correct parts if we remove header row.
 | 
			
		||||
 - let default apply if msg.delay not set in override mode. (#1397)
 | 
			
		||||
 - let trigger node be reset by boolean message (#1554)
 | 
			
		||||
 - Let trigger node support per topic mode (#1398)
 | 
			
		||||
 - let HTML node return empty array for no matching input (#1582)
 | 
			
		||||
 - MQTT node - if Server/URL config contains '//' use it as a complete url; enabled ws:// and wss://
 | 
			
		||||
 - clone messages before delayed send (#1474)
 | 
			
		||||
 - Decrement connected client count rather than show disconnected
 | 
			
		||||
 - Don't end mqtt client on first error Fixes #1566
 | 
			
		||||
 - File out - create dirs synchronously to ensure they exist Fixes #1489
 | 
			
		||||
 - Fix debug message format for Buffer (#1444)
 | 
			
		||||
 - Fix global.keys() bug in function node (#1417)
 | 
			
		||||
 - Handle escape characters in template node which uses Mustache format and JSON output mode (#1377)
 | 
			
		||||
 - Move all node.send to end of timer functions in trigger node (issue #1527) (#1539)
 | 
			
		||||
 - Publish null/undefined to mqtt as blank not toString Fixes #1521
 | 
			
		||||
 - remove inject node at specific time spinner
 | 
			
		||||
 - restrict inject interval to less that 2^31 millisecs
 | 
			
		||||
 - tag UDP ports in use properly so they get closed correctly (#1508)
 | 
			
		||||
 | 
			
		||||
#### 0.17.5: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Add express-session missing dependency for oauth
 | 
			
		||||
 - Fix improper type tests is core test cases
 | 
			
		||||
 - File node: recreate write stream when file deleted Fixes #1351
 | 
			
		||||
 - Add flow stopping trace messages
 | 
			
		||||
 - Fix userDir test case when .config.json exists (#1350)
 | 
			
		||||
 - Do not try to send msg after http request error handled Fixes #1344
 | 
			
		||||
 - Fix boundary problem in range node (#1338)
 | 
			
		||||
 - Modify messages in node properties to refer messages.json (#1339)
 | 
			
		||||
 - Fix settings.js replacing webSocketVerifyClient by webSocketNodeVerifyClient (#1343)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 0.17.4: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Add request node test case for POSTing 0
 | 
			
		||||
 - Allow false and 0 in payload for httprequest (#1334)
 | 
			
		||||
 - Add file extension into flow name of library automatically (#1331)
 | 
			
		||||
 - Fix accessing global context from jsonata expressions Fixes #1335
 | 
			
		||||
 - Disable editor whilst a deploy is inflight Fixes #1332
 | 
			
		||||
 - Replace Unknown nodes with their real versions when node loaded
 | 
			
		||||
 - Retry auto-install of modules that fail
 | 
			
		||||
 - Fix column name in link nodes to refer language file (#1330)
 | 
			
		||||
 - Use namespaces with link node title attributes i18n name Fixes #1329
 | 
			
		||||
 - Tidy up GPIO pin table presentation Fixes #1328
 | 
			
		||||
 - Join: count of 0 should not send on every msg
 | 
			
		||||
 - Handle importing only one end of a link node pair
 | 
			
		||||
 - Make sending to Debug synchronous again Fixes #1323
 | 
			
		||||
 - Make send-error behaviour optional in file node
 | 
			
		||||
 - Restore File In node behaviour of sending msg on error
 | 
			
		||||
 - Expose context.keys within Function node
 | 
			
		||||
 - JSON parser default should be not formatting output
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 0.17.3: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Fix flow library in menu to support period characters as flow name (#1320)
 | 
			
		||||
@@ -855,7 +1229,7 @@ Fixes
 | 
			
		||||
#### 0.10.10: Maintenance Release
 | 
			
		||||
 | 
			
		||||
 - Fix permissions issue with packaged nrgpio script
 | 
			
		||||
- Add better help message if deprecated node missing
 | 
			
		||||
 - Add better help message if deprecated node missing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -867,16 +1241,16 @@ Fix packaging of bin scripts
 | 
			
		||||
 | 
			
		||||
#### 0.10.8: Maintenance Release
 | 
			
		||||
 | 
			
		||||
- Nodes moved out of core
 | 
			
		||||
  - still included as a dependency: twitter, serial, email, feedparser
 | 
			
		||||
 - Nodes moved out of core
 | 
			
		||||
 - still included as a dependency: twitter, serial, email, feedparser
 | 
			
		||||
 - no longer included: mongo, arduino, irc, redis
 | 
			
		||||
- node icon defn can be a function
 | 
			
		||||
- http_proxy support
 | 
			
		||||
- httpNodeMiddleware setting
 | 
			
		||||
- Trigger node ui refresh
 | 
			
		||||
- editorTheme setting
 | 
			
		||||
- Warn on deploy of unused config nodes
 | 
			
		||||
- catch node prevents error loops
 | 
			
		||||
 - node icon defn can be a function
 | 
			
		||||
 - http_proxy support
 | 
			
		||||
 - httpNodeMiddleware setting
 | 
			
		||||
 - Trigger node ui refresh
 | 
			
		||||
 - editorTheme setting
 | 
			
		||||
 - Warn on deploy of unused config nodes
 | 
			
		||||
 - catch node prevents error loops
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -899,14 +1273,14 @@ Changes:
 | 
			
		||||
 | 
			
		||||
Changes:
 | 
			
		||||
 | 
			
		||||
- http request node passes on request url as msg.url
 | 
			
		||||
- handle config nodes appearing out of order in flow file - don't assume they are always at the start
 | 
			
		||||
- move subflow palette category to the top, to make it more obvious
 | 
			
		||||
- fix labelling of Raspberry Pi pins
 | 
			
		||||
- allow email node to mark mail as read
 | 
			
		||||
- fix saving library content
 | 
			
		||||
- add node-red and node-red-pi start scripts
 | 
			
		||||
- use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir)
 | 
			
		||||
 - http request node passes on request url as msg.url
 | 
			
		||||
 - handle config nodes appearing out of order in flow file - don't assume they are always at the start
 | 
			
		||||
 - move subflow palette category to the top, to make it more obvious
 | 
			
		||||
 - fix labelling of Raspberry Pi pins
 | 
			
		||||
 - allow email node to mark mail as read
 | 
			
		||||
 - fix saving library content
 | 
			
		||||
 - add node-red and node-red-pi start scripts
 | 
			
		||||
 - use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ We welcome contributions, but request you follow these guidelines.
 | 
			
		||||
 | 
			
		||||
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
 | 
			
		||||
By participating, you are expected to uphold this code. Please report unacceptable
 | 
			
		||||
behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
 | 
			
		||||
behavior to the project's core team at team@nodered.org.
 | 
			
		||||
 | 
			
		||||
## Raising issues
 | 
			
		||||
 | 
			
		||||
@@ -30,13 +30,13 @@ At a minimum, please include:
 | 
			
		||||
 | 
			
		||||
## Feature requests
 | 
			
		||||
 | 
			
		||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
 | 
			
		||||
For feature requests, please raise them on the [forum](https://discourse.nodered.org).
 | 
			
		||||
 | 
			
		||||
## Pull-Requests
 | 
			
		||||
 | 
			
		||||
If you want to raise a pull-request with a new feature, or a refactoring
 | 
			
		||||
of existing code, it may well get rejected if you haven't discussed it on
 | 
			
		||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
 | 
			
		||||
the [forum](https://discourse.nodered.org) first.
 | 
			
		||||
 | 
			
		||||
All contributors need to sign the JS Foundation's Contributor License Agreement.
 | 
			
		||||
It is an online process and quick to do. You can read the details of the agreement
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								Gruntfile.js
									
									
									
									
									
								
							@@ -24,6 +24,10 @@ module.exports = function(grunt) {
 | 
			
		||||
        nodemonArgs.push(flowFile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var nonHeadless = grunt.option('non-headless');
 | 
			
		||||
    if (nonHeadless) {
 | 
			
		||||
        process.env.NODE_RED_NON_HEADLESS = 'true';
 | 
			
		||||
    }
 | 
			
		||||
    grunt.initConfig({
 | 
			
		||||
        pkg: grunt.file.readJSON('package.json'),
 | 
			
		||||
        paths: {
 | 
			
		||||
@@ -41,16 +45,23 @@ module.exports = function(grunt) {
 | 
			
		||||
            core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
 | 
			
		||||
            nodes: { src: ["test/nodes/**/*_spec.js"]}
 | 
			
		||||
        },
 | 
			
		||||
        webdriver: {
 | 
			
		||||
            all: {
 | 
			
		||||
                configFile: 'test/editor/wdio.conf.js'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        mocha_istanbul: {
 | 
			
		||||
            options: {
 | 
			
		||||
                globals: ['expect'],
 | 
			
		||||
                timeout: 3000,
 | 
			
		||||
                ignoreLeaks: false,
 | 
			
		||||
                ui: 'bdd',
 | 
			
		||||
                reportFormats: ['lcov'],
 | 
			
		||||
                reportFormats: ['lcov','html'],
 | 
			
		||||
                print: 'both'
 | 
			
		||||
            },
 | 
			
		||||
            coverage: { src: ['test/**/*_spec.js'] }
 | 
			
		||||
            all: { src: ["test/_spec.js","test/red/**/*_spec.js","test/nodes/**/*_spec.js"] },
 | 
			
		||||
            core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
 | 
			
		||||
            nodes: { src: ["test/nodes/**/*_spec.js"]}
 | 
			
		||||
        },
 | 
			
		||||
        jshint: {
 | 
			
		||||
            options: {
 | 
			
		||||
@@ -137,12 +148,15 @@ module.exports = function(grunt) {
 | 
			
		||||
                    "editor/js/ui/keyboard.js",
 | 
			
		||||
                    "editor/js/ui/workspaces.js",
 | 
			
		||||
                    "editor/js/ui/view.js",
 | 
			
		||||
                    "editor/js/ui/view-navigator.js",
 | 
			
		||||
                    "editor/js/ui/sidebar.js",
 | 
			
		||||
                    "editor/js/ui/palette.js",
 | 
			
		||||
                    "editor/js/ui/tab-info.js",
 | 
			
		||||
                    "editor/js/ui/tab-config.js",
 | 
			
		||||
                    "editor/js/ui/tab-context.js",
 | 
			
		||||
                    "editor/js/ui/palette-editor.js",
 | 
			
		||||
                    "editor/js/ui/editor.js",
 | 
			
		||||
                    "editor/js/ui/editors/*.js",
 | 
			
		||||
                    "editor/js/ui/tray.js",
 | 
			
		||||
                    "editor/js/ui/clipboard.js",
 | 
			
		||||
                    "editor/js/ui/library.js",
 | 
			
		||||
@@ -151,6 +165,10 @@ module.exports = function(grunt) {
 | 
			
		||||
                    "editor/js/ui/typeSearch.js",
 | 
			
		||||
                    "editor/js/ui/subflow.js",
 | 
			
		||||
                    "editor/js/ui/userSettings.js",
 | 
			
		||||
                    "editor/js/ui/projects/projects.js",
 | 
			
		||||
                    "editor/js/ui/projects/projectSettings.js",
 | 
			
		||||
                    "editor/js/ui/projects/projectUserSettings.js",
 | 
			
		||||
                    "editor/js/ui/projects/tab-versionControl.js",
 | 
			
		||||
                    "editor/js/ui/touch/radialMenu.js"
 | 
			
		||||
                ],
 | 
			
		||||
                dest: "public/red/red.js"
 | 
			
		||||
@@ -381,9 +399,9 @@ module.exports = function(grunt) {
 | 
			
		||||
                mode: '755'
 | 
			
		||||
            },
 | 
			
		||||
            release: {
 | 
			
		||||
                // Target-specific file/dir lists and/or options go here.
 | 
			
		||||
                src: [
 | 
			
		||||
                    path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*')
 | 
			
		||||
                    path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*'),
 | 
			
		||||
                    path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/red/runtime/storage/localfilesystem/projects/git/node-red-*sh')
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
@@ -413,6 +431,7 @@ module.exports = function(grunt) {
 | 
			
		||||
    grunt.loadNpmTasks('grunt-chmod');
 | 
			
		||||
    grunt.loadNpmTasks('grunt-jsonlint');
 | 
			
		||||
    grunt.loadNpmTasks('grunt-mocha-istanbul');
 | 
			
		||||
    grunt.loadNpmTasks('grunt-webdriver');
 | 
			
		||||
 | 
			
		||||
    grunt.registerMultiTask('attachCopyright', function() {
 | 
			
		||||
        var files = this.data.src;
 | 
			
		||||
@@ -462,19 +481,23 @@ module.exports = function(grunt) {
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('default',
 | 
			
		||||
        'Builds editor content then runs code style checks and unit tests on all components',
 | 
			
		||||
        ['build','test-core','test-editor','test-nodes']);
 | 
			
		||||
        ['build','jshint:editor','mocha_istanbul:all']);
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('test-core',
 | 
			
		||||
        'Runs code style check and unit tests on core runtime code',
 | 
			
		||||
        ['jshint:core','simplemocha:core']);
 | 
			
		||||
        ['build','mocha_istanbul:core']);
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('test-editor',
 | 
			
		||||
        'Runs code style check on editor code',
 | 
			
		||||
        ['jshint:editor']);
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('test-ui',
 | 
			
		||||
        'Builds editor content then runs unit tests on editor ui',
 | 
			
		||||
        ['build','jshint:editor','webdriver:all']);
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('test-nodes',
 | 
			
		||||
        'Runs unit tests on core nodes',
 | 
			
		||||
        ['simplemocha:nodes']);
 | 
			
		||||
        ['build','mocha_istanbul:nodes']);
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('build',
 | 
			
		||||
        'Builds editor content',
 | 
			
		||||
@@ -490,5 +513,5 @@ module.exports = function(grunt) {
 | 
			
		||||
 | 
			
		||||
    grunt.registerTask('coverage',
 | 
			
		||||
        'Run Istanbul code test coverage task',
 | 
			
		||||
        ['build','mocha_istanbul']);
 | 
			
		||||
        ['build','mocha_istanbul:all']);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ A visual tool for wiring the Internet of Things.
 | 
			
		||||
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
 | 
			
		||||
started.
 | 
			
		||||
 | 
			
		||||
1. `sudo npm install -g node-red`
 | 
			
		||||
1. `sudo npm install -g --unsafe-perm node-red`
 | 
			
		||||
2. `node-red`
 | 
			
		||||
3. Open <http://localhost:1880>
 | 
			
		||||
 | 
			
		||||
@@ -22,8 +22,7 @@ started.
 | 
			
		||||
 | 
			
		||||
More documentation can be found [here](http://nodered.org/docs).
 | 
			
		||||
 | 
			
		||||
For further help, or general discussion, please use the
 | 
			
		||||
[mailing list](https://groups.google.com/forum/#!forum/node-red).
 | 
			
		||||
For further help, or general discussion, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
 | 
			
		||||
 | 
			
		||||
## Developers
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +55,7 @@ Before raising a pull-request, please read our
 | 
			
		||||
 | 
			
		||||
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
 | 
			
		||||
 By participating, you are expected to uphold this code. Please report unacceptable
 | 
			
		||||
 behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
 | 
			
		||||
 behavior to any of the project's core team at team@nodered.org.
 | 
			
		||||
 | 
			
		||||
## Authors
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ done
 | 
			
		||||
# Find the real location of this script
 | 
			
		||||
CURRENT_PATH=`pwd`
 | 
			
		||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
 | 
			
		||||
while([ -h "${SCRIPT_PATH}" ]); do
 | 
			
		||||
while [ -h "${SCRIPT_PATH}" ]; do
 | 
			
		||||
    cd "`dirname "${SCRIPT_PATH}"`"
 | 
			
		||||
    SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
 | 
			
		||||
done
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								editor/icons/batch.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/icons/batch.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 712 B  | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/file-in.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/icons/file-in.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 542 B  | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/file-out.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/icons/file-out.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 503 B  | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/sort.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/icons/sort.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 793 B  | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/env.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/images/typedInput/env.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 809 B  | 
@@ -64,27 +64,31 @@ RED.comms = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ws.onmessage = function(event) {
 | 
			
		||||
            var msg = JSON.parse(event.data);
 | 
			
		||||
            if (pendingAuth && msg.auth) {
 | 
			
		||||
                if (msg.auth === "ok") {
 | 
			
		||||
                    pendingAuth = false;
 | 
			
		||||
                    completeConnection();
 | 
			
		||||
                } else if (msg.auth === "fail") {
 | 
			
		||||
                    // anything else is an error...
 | 
			
		||||
                    active = false;
 | 
			
		||||
                    RED.user.login({updateMenu:true},function() {
 | 
			
		||||
                        connectWS();
 | 
			
		||||
                    })
 | 
			
		||||
            var message = JSON.parse(event.data);
 | 
			
		||||
            for (var m = 0; m < message.length; m++) {
 | 
			
		||||
                var msg = message[m];
 | 
			
		||||
                if (pendingAuth && msg.auth) {
 | 
			
		||||
                    if (msg.auth === "ok") {
 | 
			
		||||
                        pendingAuth = false;
 | 
			
		||||
                        completeConnection();
 | 
			
		||||
                    } else if (msg.auth === "fail") {
 | 
			
		||||
                        // anything else is an error...
 | 
			
		||||
                        active = false;
 | 
			
		||||
                        RED.user.login({updateMenu:true},function() {
 | 
			
		||||
                            connectWS();
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (msg.topic) {
 | 
			
		||||
                for (var t in subscriptions) {
 | 
			
		||||
                    if (subscriptions.hasOwnProperty(t)) {
 | 
			
		||||
                        var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
 | 
			
		||||
                        if (re.test(msg.topic)) {
 | 
			
		||||
                            var subscribers = subscriptions[t];
 | 
			
		||||
                            if (subscribers) {
 | 
			
		||||
                                for (var i=0;i<subscribers.length;i++) {
 | 
			
		||||
                                    subscribers[i](msg.topic,msg.data);
 | 
			
		||||
                else if (msg.topic) {
 | 
			
		||||
                    for (var t in subscriptions) {
 | 
			
		||||
                        if (subscriptions.hasOwnProperty(t)) {
 | 
			
		||||
                            var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
 | 
			
		||||
                            if (re.test(msg.topic)) {
 | 
			
		||||
                                var subscribers = subscriptions[t];
 | 
			
		||||
                                if (subscribers) {
 | 
			
		||||
                                    for (var i=0;i<subscribers.length;i++) {
 | 
			
		||||
                                        subscribers[i](msg.topic,msg.data);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -191,7 +191,7 @@ RED.history = (function() {
 | 
			
		||||
            } else if (ev.t == "edit") {
 | 
			
		||||
                for (i in ev.changes) {
 | 
			
		||||
                    if (ev.changes.hasOwnProperty(i)) {
 | 
			
		||||
                        if (ev.node._def.defaults[i] && ev.node._def.defaults[i].type) {
 | 
			
		||||
                        if (ev.node._def.defaults && ev.node._def.defaults[i] && ev.node._def.defaults[i].type) {
 | 
			
		||||
                            // This is a config node property
 | 
			
		||||
                            var currentConfigNode = RED.nodes.node(ev.node[i]);
 | 
			
		||||
                            if (currentConfigNode) {
 | 
			
		||||
@@ -229,10 +229,12 @@ RED.history = (function() {
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    RED.editor.validateNode(ev.node);
 | 
			
		||||
                    RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
 | 
			
		||||
                        n.inputs = ev.node.in.length;
 | 
			
		||||
                        n.outputs = ev.node.out.length;
 | 
			
		||||
                        RED.editor.updateNodeProperties(n);
 | 
			
		||||
                        RED.editor.validateNode(n);
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    var outputMap;
 | 
			
		||||
@@ -321,6 +323,9 @@ RED.history = (function() {
 | 
			
		||||
        },
 | 
			
		||||
        peek: function() {
 | 
			
		||||
            return undo_history[undo_history.length-1];
 | 
			
		||||
        },
 | 
			
		||||
        clear: function() {
 | 
			
		||||
            undo_history = [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,25 @@ RED.i18n = (function() {
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        loadCatalog: function(namespace,done) {
 | 
			
		||||
            i18n.loadNamespace(namespace,done);
 | 
			
		||||
            var languageList = i18n.functions.toLanguages(i18n.detectLanguage());
 | 
			
		||||
            var toLoad = languageList.length;
 | 
			
		||||
            languageList.forEach(function(lang) {
 | 
			
		||||
                $.ajax({
 | 
			
		||||
                    headers: {
 | 
			
		||||
                        "Accept":"application/json"
 | 
			
		||||
                    },
 | 
			
		||||
                    cache: false,
 | 
			
		||||
                    url: 'locales/'+namespace+'?lng='+lang,
 | 
			
		||||
                    success: function(data) {
 | 
			
		||||
                        i18n.addResourceBundle(lang,namespace,data);
 | 
			
		||||
                        toLoad--;
 | 
			
		||||
                        if (toLoad === 0) {
 | 
			
		||||
                            done();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        loadNodeCatalogs: function(done) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,15 @@
 | 
			
		||||
        "ctrl-g i": "core:show-info-tab",
 | 
			
		||||
        "ctrl-g d": "core:show-debug-tab",
 | 
			
		||||
        "ctrl-g c": "core:show-config-tab",
 | 
			
		||||
        "ctrl-g x": "core:show-context-tab",
 | 
			
		||||
        "ctrl-e": "core:show-export-dialog",
 | 
			
		||||
        "ctrl-i": "core:show-import-dialog",
 | 
			
		||||
        "ctrl-space": "core:toggle-sidebar",
 | 
			
		||||
        "ctrl-,": "core:show-user-settings"
 | 
			
		||||
        "ctrl-,": "core:show-user-settings",
 | 
			
		||||
        "ctrl-alt-r": "core:show-remote-diff",
 | 
			
		||||
        "ctrl-alt-n": "core:new-project",
 | 
			
		||||
        "ctrl-alt-o": "core:open-project",
 | 
			
		||||
        "ctrl-g v": "core:show-version-control-tab"
 | 
			
		||||
    },
 | 
			
		||||
    "workspace": {
 | 
			
		||||
        "backspace": "core:delete-selection",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,25 @@
 | 
			
		||||
 **/
 | 
			
		||||
(function() {
 | 
			
		||||
 | 
			
		||||
    function appendNodeConfig(nodeConfig) {
 | 
			
		||||
        var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
 | 
			
		||||
        var moduleId;
 | 
			
		||||
        if (m) {
 | 
			
		||||
            moduleId = m[1];
 | 
			
		||||
        } else {
 | 
			
		||||
            moduleId = "unknown";
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            $("body").append(nodeConfig);
 | 
			
		||||
        } catch(err) {
 | 
			
		||||
            RED.notify(RED._("notification.errors.failedToAppendNode",{module:moduleId, error:err.toString()}),{
 | 
			
		||||
                type: "error",
 | 
			
		||||
                timeout: 10000
 | 
			
		||||
            });
 | 
			
		||||
            console.log("["+moduleId+"] "+err.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function loadNodeList() {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            headers: {
 | 
			
		||||
@@ -24,7 +43,25 @@
 | 
			
		||||
            url: 'nodes',
 | 
			
		||||
            success: function(data) {
 | 
			
		||||
                RED.nodes.setNodeList(data);
 | 
			
		||||
                RED.i18n.loadNodeCatalogs(loadNodes);
 | 
			
		||||
                RED.i18n.loadNodeCatalogs(function() {
 | 
			
		||||
                    loadIconList(loadNodes);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function loadIconList(done) {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept":"application/json"
 | 
			
		||||
            },
 | 
			
		||||
            cache: false,
 | 
			
		||||
            url: 'icons',
 | 
			
		||||
            success: function(data) {
 | 
			
		||||
                RED.nodes.setIconSets(data);
 | 
			
		||||
                if (done) {
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -37,17 +74,42 @@
 | 
			
		||||
            cache: false,
 | 
			
		||||
            url: 'nodes',
 | 
			
		||||
            success: function(data) {
 | 
			
		||||
                $("body").append(data);
 | 
			
		||||
                var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/);
 | 
			
		||||
                configs.forEach(function(data) {
 | 
			
		||||
                    appendNodeConfig(data);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                $("body").i18n();
 | 
			
		||||
                $("#palette > .palette-spinner").hide();
 | 
			
		||||
                $(".palette-scroll").removeClass("hide");
 | 
			
		||||
                $("#palette-search").removeClass("hide");
 | 
			
		||||
                loadFlows();
 | 
			
		||||
                loadFlows(function() {
 | 
			
		||||
                    if (RED.settings.theme("projects.enabled",false)) {
 | 
			
		||||
                        RED.projects.refresh(function(activeProject) {
 | 
			
		||||
                            RED.sidebar.info.refresh()
 | 
			
		||||
                            if (!activeProject) {
 | 
			
		||||
                                // Projects enabled but no active project
 | 
			
		||||
                                RED.menu.setDisabled('menu-item-projects-open',true);
 | 
			
		||||
                                RED.menu.setDisabled('menu-item-projects-settings',true);
 | 
			
		||||
                                if (activeProject === false) {
 | 
			
		||||
                                    // User previously decline the migration to projects.
 | 
			
		||||
                                } else { // null/undefined
 | 
			
		||||
                                    RED.projects.showStartup();
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            completeLoad();
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Projects disabled by the user
 | 
			
		||||
                        RED.sidebar.info.refresh()
 | 
			
		||||
                        completeLoad();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function loadFlows() {
 | 
			
		||||
    function loadFlows(done) {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept":"application/json",
 | 
			
		||||
@@ -55,110 +117,257 @@
 | 
			
		||||
            cache: false,
 | 
			
		||||
            url: 'flows',
 | 
			
		||||
            success: function(nodes) {
 | 
			
		||||
                var currentHash = window.location.hash;
 | 
			
		||||
                RED.nodes.version(nodes.rev);
 | 
			
		||||
                RED.nodes.import(nodes.flows);
 | 
			
		||||
                RED.nodes.dirty(false);
 | 
			
		||||
                RED.view.redraw(true);
 | 
			
		||||
                if (/^#flow\/.+$/.test(currentHash)) {
 | 
			
		||||
                    RED.workspaces.show(currentHash.substring(6));
 | 
			
		||||
                if (nodes) {
 | 
			
		||||
                    var currentHash = window.location.hash;
 | 
			
		||||
                    RED.nodes.version(nodes.rev);
 | 
			
		||||
                    RED.nodes.import(nodes.flows);
 | 
			
		||||
                    RED.nodes.dirty(false);
 | 
			
		||||
                    RED.view.redraw(true);
 | 
			
		||||
                    if (/^#flow\/.+$/.test(currentHash)) {
 | 
			
		||||
                        RED.workspaces.show(currentHash.substring(6));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var persistentNotifications = {};
 | 
			
		||||
                RED.comms.subscribe("notification/#",function(topic,msg) {
 | 
			
		||||
                    var parts = topic.split("/");
 | 
			
		||||
                    var notificationId = parts[1];
 | 
			
		||||
                    if (notificationId === "runtime-deploy") {
 | 
			
		||||
                        // handled in ui/deploy.js
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (msg.text) {
 | 
			
		||||
                        var text = RED._(msg.text,{default:msg.text});
 | 
			
		||||
                        if (!persistentNotifications.hasOwnProperty(notificationId)) {
 | 
			
		||||
                            persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            persistentNotifications[notificationId].update(text,msg.timeout);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (persistentNotifications.hasOwnProperty(notificationId)) {
 | 
			
		||||
                        persistentNotifications[notificationId].close();
 | 
			
		||||
                        delete persistentNotifications[notificationId];
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                RED.comms.subscribe("status/#",function(topic,msg) {
 | 
			
		||||
                    var parts = topic.split("/");
 | 
			
		||||
                    var node = RED.nodes.node(parts[1]);
 | 
			
		||||
                    if (node) {
 | 
			
		||||
                        if (msg.hasOwnProperty("text")) {
 | 
			
		||||
                            if (msg.text[0] !== ".") {
 | 
			
		||||
                                msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        node.status = msg;
 | 
			
		||||
                        node.dirty = true;
 | 
			
		||||
                        RED.view.redraw();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                RED.comms.subscribe("node/#",function(topic,msg) {
 | 
			
		||||
                    var i,m;
 | 
			
		||||
                    var typeList;
 | 
			
		||||
                    var info;
 | 
			
		||||
                    if (topic == "node/added") {
 | 
			
		||||
                        var addedTypes = [];
 | 
			
		||||
                        msg.forEach(function(m) {
 | 
			
		||||
                            var id = m.id;
 | 
			
		||||
                            RED.nodes.addNodeSet(m);
 | 
			
		||||
                            addedTypes = addedTypes.concat(m.types);
 | 
			
		||||
                            RED.i18n.loadCatalog(id, function() {
 | 
			
		||||
                                $.get('nodes/'+id, function(data) {
 | 
			
		||||
                                    $("body").append(data);
 | 
			
		||||
                                });
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                        if (addedTypes.length) {
 | 
			
		||||
                            typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                            RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (topic == "node/removed") {
 | 
			
		||||
                        for (i=0;i<msg.length;i++) {
 | 
			
		||||
                            m = msg[i];
 | 
			
		||||
                            info = RED.nodes.removeNodeSet(m.id);
 | 
			
		||||
                            if (info.added) {
 | 
			
		||||
                                typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                                RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (topic == "node/enabled") {
 | 
			
		||||
                        if (msg.types) {
 | 
			
		||||
                            info = RED.nodes.getNodeSet(msg.id);
 | 
			
		||||
                            if (info.added) {
 | 
			
		||||
                                RED.nodes.enableNodeSet(msg.id);
 | 
			
		||||
                                typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                                RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                            } else {
 | 
			
		||||
                                $.get('nodes/'+msg.id, function(data) {
 | 
			
		||||
                                    $("body").append(data);
 | 
			
		||||
                                    typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                                    RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                                });
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (topic == "node/disabled") {
 | 
			
		||||
                        if (msg.types) {
 | 
			
		||||
                            RED.nodes.disableNodeSet(msg.id);
 | 
			
		||||
                            typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                            RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (topic == "node/upgraded") {
 | 
			
		||||
                        RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
 | 
			
		||||
                        RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
 | 
			
		||||
                    }
 | 
			
		||||
                    // Refresh flow library to ensure any examples are updated
 | 
			
		||||
                    RED.library.loadFlowLibrary();
 | 
			
		||||
                });
 | 
			
		||||
                done();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function completeLoad() {
 | 
			
		||||
        var persistentNotifications = {};
 | 
			
		||||
        RED.comms.subscribe("notification/#",function(topic,msg) {
 | 
			
		||||
            var parts = topic.split("/");
 | 
			
		||||
            var notificationId = parts[1];
 | 
			
		||||
            if (notificationId === "runtime-deploy") {
 | 
			
		||||
                // handled in ui/deploy.js
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (notificationId === "node") {
 | 
			
		||||
                // handled below
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (notificationId === "project-update") {
 | 
			
		||||
                RED.nodes.clear();
 | 
			
		||||
                RED.history.clear();
 | 
			
		||||
                RED.view.redraw(true);
 | 
			
		||||
                RED.projects.refresh(function() {
 | 
			
		||||
                    loadFlows(function() {
 | 
			
		||||
                        var project = RED.projects.getActiveProject();
 | 
			
		||||
                        var message = {
 | 
			
		||||
                            "change-branch": RED._("notification.project.change-branch", {project: project.git.branches.local}),
 | 
			
		||||
                            "merge-abort": RED._("notification.project.merge-abort"),
 | 
			
		||||
                            "loaded": RED._("notification.project.loaded", {project: msg.project}),
 | 
			
		||||
                            "updated": RED._("notification.project.updated", {project: msg.project}),
 | 
			
		||||
                            "pull": RED._("notification.project.pull", {project: msg.project}),
 | 
			
		||||
                            "revert": RED._("notification.project.revert", {project: msg.project}),
 | 
			
		||||
                            "merge-complete": RED._("notification.project.merge-complete")
 | 
			
		||||
                        }[msg.action];
 | 
			
		||||
                        RED.notify("<p>"+message+"</p>");
 | 
			
		||||
                        RED.sidebar.info.refresh()
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (msg.text) {
 | 
			
		||||
                msg.default = msg.text;
 | 
			
		||||
                var text = RED._(msg.text,msg);
 | 
			
		||||
                var options = {
 | 
			
		||||
                    type: msg.type,
 | 
			
		||||
                    fixed: msg.timeout === undefined,
 | 
			
		||||
                    timeout: msg.timeout,
 | 
			
		||||
                    id: notificationId
 | 
			
		||||
                }
 | 
			
		||||
                if (notificationId === "runtime-state") {
 | 
			
		||||
                    if (msg.error === "missing-types") {
 | 
			
		||||
                        text+="<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                        if (!!RED.projects.getActiveProject()) {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.manage-project-dep"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                        RED.projects.settings.show('deps');
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        // } else if (RED.settings.theme('palette.editable') !== false) {
 | 
			
		||||
                        } else {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("common.label.close"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (msg.error === "credentials_load_failed") {
 | 
			
		||||
                        if (RED.settings.theme("projects.enabled",false)) {
 | 
			
		||||
                            // projects enabled
 | 
			
		||||
                            if (RED.user.hasPermission("projects.write")) {
 | 
			
		||||
                                options.buttons = [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        text: RED._("notification.label.setup-cred"),
 | 
			
		||||
                                        click: function() {
 | 
			
		||||
                                            persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                            RED.projects.showCredentialsPrompt();
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("common.label.close"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (msg.error === "missing_flow_file") {
 | 
			
		||||
                        if (RED.user.hasPermission("projects.write")) {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.setup-project"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                        RED.projects.showFilesPrompt();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (msg.error === "missing_package_file") {
 | 
			
		||||
                        if (RED.user.hasPermission("projects.write")) {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.create-default-package"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                        RED.projects.createDefaultPackageFile();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (msg.error === "project_empty") {
 | 
			
		||||
                        if (RED.user.hasPermission("projects.write")) {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.no-thanks"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.create-default-project"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                        RED.projects.createDefaultFileSet();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (msg.error === "git_merge_conflict") {
 | 
			
		||||
                        RED.nodes.clear();
 | 
			
		||||
                        RED.sidebar.versionControl.refresh(true);
 | 
			
		||||
                        if (RED.user.hasPermission("projects.write")) {
 | 
			
		||||
                            options.buttons = [
 | 
			
		||||
                                {
 | 
			
		||||
                                    text: RED._("notification.label.show-merge-conflicts"),
 | 
			
		||||
                                    click: function() {
 | 
			
		||||
                                        persistentNotifications[notificationId].hideNotification();
 | 
			
		||||
                                        RED.sidebar.versionControl.showLocalChanges();
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (!persistentNotifications.hasOwnProperty(notificationId)) {
 | 
			
		||||
                    persistentNotifications[notificationId] = RED.notify(text,options);
 | 
			
		||||
                } else {
 | 
			
		||||
                    persistentNotifications[notificationId].update(text,options);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (persistentNotifications.hasOwnProperty(notificationId)) {
 | 
			
		||||
                persistentNotifications[notificationId].close();
 | 
			
		||||
                delete persistentNotifications[notificationId];
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        RED.comms.subscribe("status/#",function(topic,msg) {
 | 
			
		||||
            var parts = topic.split("/");
 | 
			
		||||
            var node = RED.nodes.node(parts[1]);
 | 
			
		||||
            if (node) {
 | 
			
		||||
                if (msg.hasOwnProperty("text")) {
 | 
			
		||||
                    if (msg.text[0] !== ".") {
 | 
			
		||||
                        msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                node.status = msg;
 | 
			
		||||
                node.dirty = true;
 | 
			
		||||
                RED.view.redraw();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        RED.comms.subscribe("notification/node/#",function(topic,msg) {
 | 
			
		||||
            var i,m;
 | 
			
		||||
            var typeList;
 | 
			
		||||
            var info;
 | 
			
		||||
            if (topic == "notification/node/added") {
 | 
			
		||||
                var addedTypes = [];
 | 
			
		||||
                msg.forEach(function(m) {
 | 
			
		||||
                    var id = m.id;
 | 
			
		||||
                    RED.nodes.addNodeSet(m);
 | 
			
		||||
                    addedTypes = addedTypes.concat(m.types);
 | 
			
		||||
                    RED.i18n.loadCatalog(id, function() {
 | 
			
		||||
                        $.get('nodes/'+id, function(data) {
 | 
			
		||||
                            appendNodeConfig(data);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
                if (addedTypes.length) {
 | 
			
		||||
                    typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                    RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
 | 
			
		||||
                }
 | 
			
		||||
                loadIconList();
 | 
			
		||||
            } else if (topic == "notification/node/removed") {
 | 
			
		||||
                for (i=0;i<msg.length;i++) {
 | 
			
		||||
                    m = msg[i];
 | 
			
		||||
                    info = RED.nodes.removeNodeSet(m.id);
 | 
			
		||||
                    if (info.added) {
 | 
			
		||||
                        typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                        RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                loadIconList();
 | 
			
		||||
            } else if (topic == "notification/node/enabled") {
 | 
			
		||||
                if (msg.types) {
 | 
			
		||||
                    info = RED.nodes.getNodeSet(msg.id);
 | 
			
		||||
                    if (info.added) {
 | 
			
		||||
                        RED.nodes.enableNodeSet(msg.id);
 | 
			
		||||
                        typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                        RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $.get('nodes/'+msg.id, function(data) {
 | 
			
		||||
                            appendNodeConfig(data);
 | 
			
		||||
                            typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                            RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (topic == "notification/node/disabled") {
 | 
			
		||||
                if (msg.types) {
 | 
			
		||||
                    RED.nodes.disableNodeSet(msg.id);
 | 
			
		||||
                    typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
 | 
			
		||||
                    RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
 | 
			
		||||
                }
 | 
			
		||||
            } else if (topic == "node/upgraded") {
 | 
			
		||||
                RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
 | 
			
		||||
                RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
 | 
			
		||||
            }
 | 
			
		||||
            // Refresh flow library to ensure any examples are updated
 | 
			
		||||
            RED.library.loadFlowLibrary();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showAbout() {
 | 
			
		||||
        $.get('red/about', function(data) {
 | 
			
		||||
            var aboutHeader = '<div style="text-align:center;">'+
 | 
			
		||||
@@ -172,6 +381,15 @@
 | 
			
		||||
 | 
			
		||||
    function loadEditor() {
 | 
			
		||||
        var menuOptions = [];
 | 
			
		||||
        if (RED.settings.theme("projects.enabled",false)) {
 | 
			
		||||
            menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[
 | 
			
		||||
                {id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"},
 | 
			
		||||
                {id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"},
 | 
			
		||||
                {id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
 | 
			
		||||
            ]});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
 | 
			
		||||
            // {id:"menu-item-view-show-grid",setting:"view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
 | 
			
		||||
            // {id:"menu-item-view-snap-grid",setting:"view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
 | 
			
		||||
@@ -192,8 +410,8 @@
 | 
			
		||||
            {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"},
 | 
			
		||||
            {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
 | 
			
		||||
        ]});
 | 
			
		||||
        menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[
 | 
			
		||||
            {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:show-export-dialog"},
 | 
			
		||||
        menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),options:[
 | 
			
		||||
            {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-export-dialog"},
 | 
			
		||||
            {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"}
 | 
			
		||||
        ]});
 | 
			
		||||
        menuOptions.push(null);
 | 
			
		||||
@@ -215,12 +433,12 @@
 | 
			
		||||
            menuOptions.push(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.userSettings"),onselect:"core:show-user-settings"});
 | 
			
		||||
        menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.settings"),onselect:"core:show-user-settings"});
 | 
			
		||||
        menuOptions.push(null);
 | 
			
		||||
 | 
			
		||||
        menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
 | 
			
		||||
        menuOptions.push({id:"menu-item-help",
 | 
			
		||||
            label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"),
 | 
			
		||||
            label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
 | 
			
		||||
            href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
 | 
			
		||||
        });
 | 
			
		||||
        menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
 | 
			
		||||
@@ -234,9 +452,18 @@
 | 
			
		||||
        RED.palette.init();
 | 
			
		||||
        if (RED.settings.theme('palette.editable') !== false) {
 | 
			
		||||
            RED.palette.editor.init();
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("Palette editor disabled");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RED.sidebar.init();
 | 
			
		||||
 | 
			
		||||
        if (RED.settings.theme("projects.enabled",false)) {
 | 
			
		||||
            RED.projects.init();
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("Projects disabled");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RED.subflow.init();
 | 
			
		||||
        RED.workspaces.init();
 | 
			
		||||
        RED.clipboard.init();
 | 
			
		||||
@@ -247,15 +474,15 @@
 | 
			
		||||
        RED.menu.init({id:"btn-sidemenu",options: menuOptions});
 | 
			
		||||
 | 
			
		||||
        RED.deploy.init(RED.settings.theme("deployButton",null));
 | 
			
		||||
        RED.notifications.init();
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:show-about", showAbout);
 | 
			
		||||
 | 
			
		||||
        RED.nodes.init();
 | 
			
		||||
        RED.comms.connect();
 | 
			
		||||
 | 
			
		||||
        $("#main-container").show();
 | 
			
		||||
        $(".header-toolbar").show();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        loadNodeList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,16 @@ RED.nodes = (function() {
 | 
			
		||||
        var nodeSets = {};
 | 
			
		||||
        var typeToId = {};
 | 
			
		||||
        var nodeDefinitions = {};
 | 
			
		||||
        var iconSets = {};
 | 
			
		||||
 | 
			
		||||
        nodeDefinitions['tab'] = {
 | 
			
		||||
            defaults: {
 | 
			
		||||
                label: {value:""},
 | 
			
		||||
                disabled: {value: false},
 | 
			
		||||
                info: {value: ""}
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var exports = {
 | 
			
		||||
            setModulePendingUpdated: function(module,version) {
 | 
			
		||||
@@ -123,7 +133,7 @@ RED.nodes = (function() {
 | 
			
		||||
            registerNodeType: function(nt,def) {
 | 
			
		||||
                nodeDefinitions[nt] = def;
 | 
			
		||||
                def.type = nt;
 | 
			
		||||
                if (def.category != "subflows") {
 | 
			
		||||
                if (nt.substring(0,8) != "subflow:") {
 | 
			
		||||
                    def.set = nodeSets[typeToId[nt]];
 | 
			
		||||
                    nodeSets[typeToId[nt]].added = true;
 | 
			
		||||
                    nodeSets[typeToId[nt]].enabled = true;
 | 
			
		||||
@@ -161,6 +171,12 @@ RED.nodes = (function() {
 | 
			
		||||
            },
 | 
			
		||||
            getNodeType: function(nt) {
 | 
			
		||||
                return nodeDefinitions[nt];
 | 
			
		||||
            },
 | 
			
		||||
            setIconSets: function(sets) {
 | 
			
		||||
                iconSets = sets;
 | 
			
		||||
            },
 | 
			
		||||
            getIconSets: function() {
 | 
			
		||||
                return iconSets;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        return exports;
 | 
			
		||||
@@ -256,10 +272,19 @@ RED.nodes = (function() {
 | 
			
		||||
                if (updatedConfigNode) {
 | 
			
		||||
                    RED.workspaces.refresh();
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    if (node._def.oneditdelete) {
 | 
			
		||||
                        node._def.oneditdelete.call(node);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch(err) {
 | 
			
		||||
                    console.log("oneditdelete",node.id,node.type,err.toString());
 | 
			
		||||
                }
 | 
			
		||||
                RED.events.emit('nodes:remove',node);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (node && node._def.onremove) {
 | 
			
		||||
            // Deprecated: never documented but used by some early nodes
 | 
			
		||||
            console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditremove - please report");
 | 
			
		||||
            node._def.onremove.call(n);
 | 
			
		||||
        }
 | 
			
		||||
        return {links:removedLinks,nodes:removedNodes};
 | 
			
		||||
@@ -274,14 +299,7 @@ RED.nodes = (function() {
 | 
			
		||||
 | 
			
		||||
    function addWorkspace(ws) {
 | 
			
		||||
        workspaces[ws.id] = ws;
 | 
			
		||||
        ws._def = {
 | 
			
		||||
            defaults: {
 | 
			
		||||
                label: {value:""},
 | 
			
		||||
                disabled: {value: false},
 | 
			
		||||
                info: {value: ""}
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ws._def = RED.nodes.getType('tab');
 | 
			
		||||
        workspacesOrder.push(ws.id);
 | 
			
		||||
    }
 | 
			
		||||
    function getWorkspace(id) {
 | 
			
		||||
@@ -337,8 +355,8 @@ RED.nodes = (function() {
 | 
			
		||||
        RED.nodes.registerType("subflow:"+sf.id, {
 | 
			
		||||
            defaults:{name:{value:""}},
 | 
			
		||||
            info: sf.info,
 | 
			
		||||
            icon:"subflow.png",
 | 
			
		||||
            category: "subflows",
 | 
			
		||||
            icon: function() { return sf.icon||"subflow.png" },
 | 
			
		||||
            category: sf.category || "subflows",
 | 
			
		||||
            inputs: sf.in.length,
 | 
			
		||||
            outputs: sf.out.length,
 | 
			
		||||
            color: "#da9",
 | 
			
		||||
@@ -473,7 +491,9 @@ RED.nodes = (function() {
 | 
			
		||||
            for (var j=0;j<wires.length;j++) {
 | 
			
		||||
                var w = wires[j];
 | 
			
		||||
                if (w.target.type != "subflow") {
 | 
			
		||||
                    node.wires[w.sourcePort].push(w.target.id);
 | 
			
		||||
                    if (w.sourcePort < node.wires.length) {
 | 
			
		||||
                        node.wires[w.sourcePort].push(w.target.id);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -483,6 +503,12 @@ RED.nodes = (function() {
 | 
			
		||||
            if (n.outputs > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
 | 
			
		||||
                node.outputLabels = n.outputLabels.slice();
 | 
			
		||||
            }
 | 
			
		||||
            if ((!n._def.defaults || !n._def.defaults.hasOwnProperty("icon")) && n.icon) {
 | 
			
		||||
                var defIcon = RED.utils.getDefaultNodeIcon(n._def, n);
 | 
			
		||||
                if (n.icon !== defIcon.module+"/"+defIcon.file) {
 | 
			
		||||
                    node.icon = n.icon;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
@@ -493,6 +519,7 @@ RED.nodes = (function() {
 | 
			
		||||
        node.type = n.type;
 | 
			
		||||
        node.name = n.name;
 | 
			
		||||
        node.info = n.info;
 | 
			
		||||
        node.category = n.category;
 | 
			
		||||
        node.in = [];
 | 
			
		||||
        node.out = [];
 | 
			
		||||
 | 
			
		||||
@@ -526,7 +553,11 @@ RED.nodes = (function() {
 | 
			
		||||
        if (node.out.length > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
 | 
			
		||||
            node.outputLabels = n.outputLabels.slice();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (n.icon) {
 | 
			
		||||
            if (n.icon !== "node-red/subflow.png") {
 | 
			
		||||
                node.icon = n.icon;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
@@ -699,7 +730,9 @@ RED.nodes = (function() {
 | 
			
		||||
        if (!$.isArray(newNodes)) {
 | 
			
		||||
            newNodes = [newNodes];
 | 
			
		||||
        }
 | 
			
		||||
        var isInitialLoad = false;
 | 
			
		||||
        if (!initialLoad) {
 | 
			
		||||
            isInitialLoad = true;
 | 
			
		||||
            initialLoad = JSON.parse(JSON.stringify(newNodes));
 | 
			
		||||
        }
 | 
			
		||||
        var unknownTypes = [];
 | 
			
		||||
@@ -720,10 +753,10 @@ RED.nodes = (function() {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        if (unknownTypes.length > 0) {
 | 
			
		||||
        if (!isInitialLoad && unknownTypes.length > 0) {
 | 
			
		||||
            var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
 | 
			
		||||
            var type = "type"+(unknownTypes.length > 1?"s":"");
 | 
			
		||||
            RED.notify("<strong>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</strong>"+typeList,"error",false,10000);
 | 
			
		||||
            RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var activeWorkspace = RED.workspaces.active();
 | 
			
		||||
@@ -817,7 +850,7 @@ RED.nodes = (function() {
 | 
			
		||||
 | 
			
		||||
        // Add a tab if there isn't one there already
 | 
			
		||||
        if (defaultWorkspace == null) {
 | 
			
		||||
            defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})};
 | 
			
		||||
            defaultWorkspace = { type:"tab", id:getID(), disabled: false, info:"",  label:RED._('workspace.defaultName',{number:1})};
 | 
			
		||||
            addWorkspace(defaultWorkspace);
 | 
			
		||||
            RED.workspaces.add(defaultWorkspace);
 | 
			
		||||
            new_workspaces.push(defaultWorkspace);
 | 
			
		||||
@@ -870,7 +903,7 @@ RED.nodes = (function() {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) {
 | 
			
		||||
                if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
 | 
			
		||||
                    configNode = {id:n.id, z:n.z, type:n.type, users:[], _config:{}};
 | 
			
		||||
                    for (d in def.defaults) {
 | 
			
		||||
                        if (def.defaults.hasOwnProperty(d)) {
 | 
			
		||||
@@ -913,6 +946,7 @@ RED.nodes = (function() {
 | 
			
		||||
                        wires:n.wires,
 | 
			
		||||
                        inputLabels: n.inputLabels,
 | 
			
		||||
                        outputLabels: n.outputLabels,
 | 
			
		||||
                        icon: n.icon,
 | 
			
		||||
                        changed:false,
 | 
			
		||||
                        _config:{}
 | 
			
		||||
                    };
 | 
			
		||||
@@ -981,6 +1015,13 @@ RED.nodes = (function() {
 | 
			
		||||
                                    set: registry.getNodeSet("node-red/unknown")
 | 
			
		||||
                                };
 | 
			
		||||
                                node.users = [];
 | 
			
		||||
                                // This is a config node, so delete the default
 | 
			
		||||
                                // non-config node properties
 | 
			
		||||
                                delete node.x;
 | 
			
		||||
                                delete node.y;
 | 
			
		||||
                                delete node.wires;
 | 
			
		||||
                                delete node.inputLabels;
 | 
			
		||||
                                delete node.outputLabels;
 | 
			
		||||
                            }
 | 
			
		||||
                            var orig = {};
 | 
			
		||||
                            for (var p in n) {
 | 
			
		||||
@@ -993,10 +1034,31 @@ RED.nodes = (function() {
 | 
			
		||||
                            node.type = "unknown";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (node._def.category != "config") {
 | 
			
		||||
                            node.inputs = n.inputs||node._def.inputs;
 | 
			
		||||
                            node.outputs = n.outputs||node._def.outputs;
 | 
			
		||||
                            if (n.hasOwnProperty('inputs')) {
 | 
			
		||||
                                node.inputs = n.inputs;
 | 
			
		||||
                                node._config.inputs = JSON.stringify(n.inputs);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                node.inputs = node._def.inputs;
 | 
			
		||||
                            }
 | 
			
		||||
                            if (n.hasOwnProperty('outputs')) {
 | 
			
		||||
                                node.outputs = n.outputs;
 | 
			
		||||
                                node._config.outputs = JSON.stringify(n.outputs);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                node.outputs = node._def.outputs;
 | 
			
		||||
                            }
 | 
			
		||||
                            if (node.hasOwnProperty('wires') && node.wires.length > node.outputs) {
 | 
			
		||||
                                if (!node._def.defaults.hasOwnProperty("outputs") || !isNaN(parseInt(n.outputs))) {
 | 
			
		||||
                                    // If 'wires' is longer than outputs, clip wires
 | 
			
		||||
                                    console.log("Warning: node.wires longer than node.outputs - trimming wires:",node.id," wires:",node.wires.length," outputs:",node.outputs);
 | 
			
		||||
                                    node.wires = node.wires.slice(0,node.outputs);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    // The node declares outputs in its defaults, but has not got a valid value
 | 
			
		||||
                                    // Defer to the length of the wires array
 | 
			
		||||
                                    node.outputs = node.wires.length;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            for (d in node._def.defaults) {
 | 
			
		||||
                                if (node._def.defaults.hasOwnProperty(d)) {
 | 
			
		||||
                                if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
 | 
			
		||||
                                    node[d] = n[d];
 | 
			
		||||
                                    node._config[d] = JSON.stringify(n[d]);
 | 
			
		||||
                                }
 | 
			
		||||
@@ -1016,7 +1078,9 @@ RED.nodes = (function() {
 | 
			
		||||
                    addNode(node);
 | 
			
		||||
                    RED.editor.validateNode(node);
 | 
			
		||||
                    node_map[n.id] = node;
 | 
			
		||||
                    if (node._def.category != "config") {
 | 
			
		||||
                    // If an 'unknown' config node, it will not have been caught by the
 | 
			
		||||
                    // proper config node handling, so needs adding to new_nodes here
 | 
			
		||||
                    if (node.type === "unknown" || node._def.category !== "config") {
 | 
			
		||||
                        new_nodes.push(node);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -1198,12 +1262,13 @@ RED.nodes = (function() {
 | 
			
		||||
            RED.workspaces.remove(workspaces[id]);
 | 
			
		||||
        });
 | 
			
		||||
        defaultWorkspace = null;
 | 
			
		||||
 | 
			
		||||
        RED.nodes.dirty(true);
 | 
			
		||||
        initialLoad = null;
 | 
			
		||||
        RED.nodes.dirty(false);
 | 
			
		||||
        RED.view.redraw(true);
 | 
			
		||||
        RED.palette.refresh();
 | 
			
		||||
        RED.workspaces.refresh();
 | 
			
		||||
        RED.sidebar.config.refresh();
 | 
			
		||||
        RED.sidebar.info.refresh();
 | 
			
		||||
 | 
			
		||||
        // var node_defs = {};
 | 
			
		||||
        // var nodes = [];
 | 
			
		||||
@@ -1217,6 +1282,50 @@ RED.nodes = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            RED.events.on("registry:node-type-added",function(type) {
 | 
			
		||||
                var def = registry.getNodeType(type);
 | 
			
		||||
                var replaced = false;
 | 
			
		||||
                var replaceNodes = [];
 | 
			
		||||
                RED.nodes.eachNode(function(n) {
 | 
			
		||||
                    if (n.type === "unknown" && n.name === type) {
 | 
			
		||||
                        replaceNodes.push(n);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                RED.nodes.eachConfig(function(n) {
 | 
			
		||||
                    if (n.type === "unknown" && n.name === type) {
 | 
			
		||||
                        replaceNodes.push(n);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                if (replaceNodes.length > 0) {
 | 
			
		||||
                    var reimportList = [];
 | 
			
		||||
                    replaceNodes.forEach(function(n) {
 | 
			
		||||
                        if (configNodes.hasOwnProperty(n.id)) {
 | 
			
		||||
                            delete configNodes[n.id];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            nodes.splice(nodes.indexOf(n),1);
 | 
			
		||||
                        }
 | 
			
		||||
                        reimportList.push(convertNode(n));
 | 
			
		||||
                    });
 | 
			
		||||
                    RED.view.redraw(true);
 | 
			
		||||
                    var result = importNodes(reimportList,false);
 | 
			
		||||
                    var newNodeMap = {};
 | 
			
		||||
                    result[0].forEach(function(n) {
 | 
			
		||||
                        newNodeMap[n.id] = n;
 | 
			
		||||
                    });
 | 
			
		||||
                    RED.nodes.eachLink(function(l) {
 | 
			
		||||
                        if (newNodeMap.hasOwnProperty(l.source.id)) {
 | 
			
		||||
                            l.source = newNodeMap[l.source.id];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (newNodeMap.hasOwnProperty(l.target.id)) {
 | 
			
		||||
                            l.target = newNodeMap[l.target.id];
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    RED.view.redraw(true);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        registry:registry,
 | 
			
		||||
        setNodeList: registry.setNodeList,
 | 
			
		||||
 | 
			
		||||
@@ -1226,6 +1335,9 @@ RED.nodes = (function() {
 | 
			
		||||
        enableNodeSet: registry.enableNodeSet,
 | 
			
		||||
        disableNodeSet: registry.disableNodeSet,
 | 
			
		||||
 | 
			
		||||
        setIconSets: registry.setIconSets,
 | 
			
		||||
        getIconSets: registry.getIconSets,
 | 
			
		||||
 | 
			
		||||
        registerType: registry.registerNodeType,
 | 
			
		||||
        getType: registry.getNodeType,
 | 
			
		||||
        convertNode: convertNode,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,9 @@
 | 
			
		||||
RED.settings = (function () {
 | 
			
		||||
 | 
			
		||||
    var loadedSettings = {};
 | 
			
		||||
    var userSettings = {};
 | 
			
		||||
    var settingsDirty = false;
 | 
			
		||||
    var pendingSave;
 | 
			
		||||
 | 
			
		||||
    var hasLocalStorage = function () {
 | 
			
		||||
        try {
 | 
			
		||||
@@ -31,7 +34,12 @@ RED.settings = (function () {
 | 
			
		||||
        if (!hasLocalStorage()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        localStorage.setItem(key, JSON.stringify(value));
 | 
			
		||||
        if (key === "auth-tokens") {
 | 
			
		||||
            localStorage.setItem(key, JSON.stringify(value));
 | 
			
		||||
        } else {
 | 
			
		||||
            userSettings[key] = value;
 | 
			
		||||
            saveUserSettings();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -44,14 +52,23 @@ RED.settings = (function () {
 | 
			
		||||
        if (!hasLocalStorage()) {
 | 
			
		||||
            return undefined;
 | 
			
		||||
        }
 | 
			
		||||
        return JSON.parse(localStorage.getItem(key));
 | 
			
		||||
        if (key === "auth-tokens") {
 | 
			
		||||
            return JSON.parse(localStorage.getItem(key));
 | 
			
		||||
        } else {
 | 
			
		||||
            return userSettings[key];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var remove = function (key) {
 | 
			
		||||
        if (!hasLocalStorage()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        localStorage.removeItem(key);
 | 
			
		||||
        if (key === "auth-tokens") {
 | 
			
		||||
            localStorage.removeItem(key);
 | 
			
		||||
        } else {
 | 
			
		||||
            delete userSettings[key];
 | 
			
		||||
            saveUserSettings();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var setProperties = function(data) {
 | 
			
		||||
@@ -68,6 +85,10 @@ RED.settings = (function () {
 | 
			
		||||
        loadedSettings = data;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var setUserSettings = function(data) {
 | 
			
		||||
        userSettings = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var init = function (done) {
 | 
			
		||||
        var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
 | 
			
		||||
        if (accessTokenMatch) {
 | 
			
		||||
@@ -106,7 +127,7 @@ RED.settings = (function () {
 | 
			
		||||
                    RED.settings.remove("auth-tokens");
 | 
			
		||||
                }
 | 
			
		||||
                console.log("Node-RED: " + data.version);
 | 
			
		||||
                done();
 | 
			
		||||
                loadUserSettings(done);
 | 
			
		||||
            },
 | 
			
		||||
            error: function(jqXHR,textStatus,errorThrown) {
 | 
			
		||||
                if (jqXHR.status === 401) {
 | 
			
		||||
@@ -115,12 +136,52 @@ RED.settings = (function () {
 | 
			
		||||
                    }
 | 
			
		||||
                    RED.user.login(function() { load(done); });
 | 
			
		||||
                } else {
 | 
			
		||||
                    console.log("Unexpected error:",jqXHR.status,textStatus);
 | 
			
		||||
                    console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function loadUserSettings(done) {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            },
 | 
			
		||||
            dataType: "json",
 | 
			
		||||
            cache: false,
 | 
			
		||||
            url: 'settings/user',
 | 
			
		||||
            success: function (data) {
 | 
			
		||||
                setUserSettings(data);
 | 
			
		||||
                done();
 | 
			
		||||
            },
 | 
			
		||||
            error: function(jqXHR,textStatus,errorThrown) {
 | 
			
		||||
                console.log("Unexpected error loading user settings:",jqXHR.status,textStatus);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function saveUserSettings() {
 | 
			
		||||
        if (RED.user.hasPermission("settings.write")) {
 | 
			
		||||
            if (pendingSave) {
 | 
			
		||||
                clearTimeout(pendingSave);
 | 
			
		||||
            }
 | 
			
		||||
            pendingSave = setTimeout(function() {
 | 
			
		||||
                pendingSave = null;
 | 
			
		||||
                $.ajax({
 | 
			
		||||
                    method: 'POST',
 | 
			
		||||
                    contentType: 'application/json',
 | 
			
		||||
                    url: 'settings/user',
 | 
			
		||||
                    data: JSON.stringify(userSettings),
 | 
			
		||||
                    success: function (data) {
 | 
			
		||||
                    },
 | 
			
		||||
                    error: function(jqXHR,textStatus,errorThrown) {
 | 
			
		||||
                        console.log("Unexpected error saving user settings:",jqXHR.status,textStatus);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            },300);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function theme(property,defaultValue) {
 | 
			
		||||
        if (!RED.settings.editorTheme) {
 | 
			
		||||
            return defaultValue;
 | 
			
		||||
@@ -143,10 +204,10 @@ RED.settings = (function () {
 | 
			
		||||
    return {
 | 
			
		||||
        init: init,
 | 
			
		||||
        load: load,
 | 
			
		||||
        loadUserSettings: loadUserSettings,
 | 
			
		||||
        set: set,
 | 
			
		||||
        get: get,
 | 
			
		||||
        remove: remove,
 | 
			
		||||
        theme: theme
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
();
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -203,7 +203,8 @@ RED.clipboard = (function() {
 | 
			
		||||
            var nodes = null;
 | 
			
		||||
            if (type === 'export-range-selected') {
 | 
			
		||||
                var selection = RED.view.selection();
 | 
			
		||||
                nodes = RED.nodes.createExportableNodeSet(selection.nodes);
 | 
			
		||||
                // Don't include the subflow meta-port nodes in the exported selection
 | 
			
		||||
                nodes = RED.nodes.createExportableNodeSet(selection.nodes.filter(function(n) { return n.type !== 'subflow'}));
 | 
			
		||||
            } else if (type === 'export-range-flow') {
 | 
			
		||||
                var activeWorkspace = RED.workspaces.active();
 | 
			
		||||
                nodes = RED.nodes.filterNodes({z:activeWorkspace});
 | 
			
		||||
@@ -275,9 +276,20 @@ RED.clipboard = (function() {
 | 
			
		||||
        if (typeof value !== "string" ) {
 | 
			
		||||
            value = JSON.stringify(value, function(key,value) {
 | 
			
		||||
                if (value !== null && typeof value === 'object') {
 | 
			
		||||
                    if (value.__encoded__ && value.hasOwnProperty('data') && value.hasOwnProperty('length')) {
 | 
			
		||||
                        truncated = value.data.length !== value.length;
 | 
			
		||||
                        return value.data;
 | 
			
		||||
                    if (value.__enc__) {
 | 
			
		||||
                        if (value.hasOwnProperty('data') && value.hasOwnProperty('length')) {
 | 
			
		||||
                            truncated = value.data.length !== value.length;
 | 
			
		||||
                            return value.data;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (value.type === 'function' || value.type === 'internal') {
 | 
			
		||||
                            return undefined
 | 
			
		||||
                        }
 | 
			
		||||
                        if (value.type === 'number') {
 | 
			
		||||
                            // Handle NaN and Infinity - they are not permitted
 | 
			
		||||
                            // in JSON. We can either substitute with a String
 | 
			
		||||
                            // representation or null
 | 
			
		||||
                            return null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return value;
 | 
			
		||||
@@ -308,18 +320,6 @@ RED.clipboard = (function() {
 | 
			
		||||
 | 
			
		||||
            $('<input type="text" id="clipboard-hidden">').appendTo("body");
 | 
			
		||||
 | 
			
		||||
            RED.events.on("view:selection-changed",function(selection) {
 | 
			
		||||
                if (!selection.nodes) {
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export",true);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-clipboard",true);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-library",true);
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export",false);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-clipboard",false);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-library",false);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            RED.actions.add("core:show-export-dialog",exportNodes);
 | 
			
		||||
            RED.actions.add("core:show-import-dialog",importNodes);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@
 | 
			
		||||
                        addLabel = 'add';
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                $('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
 | 
			
		||||
                $('<a href="#" class="editor-button editor-button-small red-ui-editableList-addButton" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
 | 
			
		||||
                    .appendTo(this.topContainer)
 | 
			
		||||
                    .click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
@@ -85,7 +85,7 @@
 | 
			
		||||
            if (this.element.css("position") === "absolute") {
 | 
			
		||||
                ["top","left","bottom","right"].forEach(function(s) {
 | 
			
		||||
                    var v = that.element.css(s);
 | 
			
		||||
                    if (s!=="auto" && s!=="") {
 | 
			
		||||
                    if (v!=="auto" && v!=="") {
 | 
			
		||||
                        that.topContainer.css(s,v);
 | 
			
		||||
                        that.uiContainer.css(s,"0");
 | 
			
		||||
                        that.element.css(s,'auto');
 | 
			
		||||
@@ -116,6 +116,11 @@
 | 
			
		||||
                this.uiContainer.css("minHeight",minHeight);
 | 
			
		||||
                this.element.css("minHeight",0);
 | 
			
		||||
            }
 | 
			
		||||
            var maxHeight = this.element.css("maxHeight");
 | 
			
		||||
            if (maxHeight !== '0px') {
 | 
			
		||||
                this.uiContainer.css("maxHeight",maxHeight);
 | 
			
		||||
                this.element.css("maxHeight",null);
 | 
			
		||||
            }
 | 
			
		||||
            if (this.options.height !== 'auto') {
 | 
			
		||||
                this.uiContainer.css("overflow-y","scroll");
 | 
			
		||||
                if (!isNaN(this.options.height)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,14 @@ RED.popover = (function() {
 | 
			
		||||
        "default": {
 | 
			
		||||
            top: 10,
 | 
			
		||||
            leftRight: 17,
 | 
			
		||||
            leftLeft: 25
 | 
			
		||||
            leftLeft: 25,
 | 
			
		||||
            leftBottom: 8,
 | 
			
		||||
        },
 | 
			
		||||
        "small": {
 | 
			
		||||
            top: 5,
 | 
			
		||||
            leftRight: 8,
 | 
			
		||||
            leftLeft: 16
 | 
			
		||||
            leftRight: 17,
 | 
			
		||||
            leftLeft: 16,
 | 
			
		||||
            leftBottom: 3,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function createPopover(options) {
 | 
			
		||||
@@ -33,6 +35,7 @@ RED.popover = (function() {
 | 
			
		||||
        var trigger = options.trigger;
 | 
			
		||||
        var content = options.content;
 | 
			
		||||
        var delay = options.delay;
 | 
			
		||||
        var autoClose = options.autoClose;
 | 
			
		||||
        var width = options.width||"auto";
 | 
			
		||||
        var size = options.size||"default";
 | 
			
		||||
        if (!deltaSizes[size]) {
 | 
			
		||||
@@ -43,50 +46,65 @@ RED.popover = (function() {
 | 
			
		||||
        var active;
 | 
			
		||||
        var div;
 | 
			
		||||
 | 
			
		||||
        var openPopup = function() {
 | 
			
		||||
        var openPopup = function(instant) {
 | 
			
		||||
            if (active) {
 | 
			
		||||
                div = $('<div class="red-ui-popover red-ui-popover-'+direction+'"></div>').appendTo("body");
 | 
			
		||||
                div = $('<div class="red-ui-popover red-ui-popover-'+direction+'"></div>');
 | 
			
		||||
                if (size !== "default") {
 | 
			
		||||
                    div.addClass("red-ui-popover-size-"+size);
 | 
			
		||||
                }
 | 
			
		||||
                if (typeof content === 'function') {
 | 
			
		||||
                    content.call(res).appendTo(div);
 | 
			
		||||
                    var result = content.call(res);
 | 
			
		||||
                    if (result === null) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (typeof result === 'string') {
 | 
			
		||||
                        div.text(result);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        div.append(result);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    div.html(content);
 | 
			
		||||
                }
 | 
			
		||||
                if (width !== "auto") {
 | 
			
		||||
                    div.width(width);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                div.appendTo("body");
 | 
			
		||||
 | 
			
		||||
                var targetPos = target.offset();
 | 
			
		||||
                var targetWidth = target.width();
 | 
			
		||||
                var targetHeight = target.height();
 | 
			
		||||
 | 
			
		||||
                var targetWidth = target.outerWidth();
 | 
			
		||||
                var targetHeight = target.outerHeight();
 | 
			
		||||
                var divHeight = div.height();
 | 
			
		||||
                var divWidth = div.width();
 | 
			
		||||
                if (direction === 'right') {
 | 
			
		||||
                    div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left+targetWidth+deltaSizes[size].leftRight});
 | 
			
		||||
                } else if (direction === 'left') {
 | 
			
		||||
                    div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left-deltaSizes[size].leftLeft-divWidth});
 | 
			
		||||
                } else if (direction === 'bottom') {
 | 
			
		||||
                    div.css({top: targetPos.top+targetHeight+deltaSizes[size].top,left:targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom});
 | 
			
		||||
                }
 | 
			
		||||
                if (instant) {
 | 
			
		||||
                    div.show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    div.fadeIn("fast");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                div.fadeIn("fast");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        var closePopup = function() {
 | 
			
		||||
        var closePopup = function(instant) {
 | 
			
		||||
            if (!active) {
 | 
			
		||||
                if (div) {
 | 
			
		||||
                    div.fadeOut("fast",function() {
 | 
			
		||||
                    if (instant) {
 | 
			
		||||
                        $(this).remove();
 | 
			
		||||
                    });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        div.fadeOut("fast",function() {
 | 
			
		||||
                            $(this).remove();
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    div = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (trigger === 'hover') {
 | 
			
		||||
 | 
			
		||||
            target.on('mouseenter',function(e) {
 | 
			
		||||
                clearTimeout(timer);
 | 
			
		||||
                active = true;
 | 
			
		||||
@@ -110,18 +128,26 @@ RED.popover = (function() {
 | 
			
		||||
                    openPopup();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } else if (autoClose) {
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                active = false;
 | 
			
		||||
                closePopup();
 | 
			
		||||
            },autoClose);
 | 
			
		||||
        }
 | 
			
		||||
        var res = {
 | 
			
		||||
            setContent: function(_content) {
 | 
			
		||||
                content = _content;
 | 
			
		||||
                return res;
 | 
			
		||||
            },
 | 
			
		||||
            open: function () {
 | 
			
		||||
            open: function (instant) {
 | 
			
		||||
                active = true;
 | 
			
		||||
                openPopup();
 | 
			
		||||
                openPopup(instant);
 | 
			
		||||
                return res;
 | 
			
		||||
            },
 | 
			
		||||
            close: function () {
 | 
			
		||||
            close: function (instant) {
 | 
			
		||||
                active = false;
 | 
			
		||||
                closePopup();
 | 
			
		||||
                closePopup(instant);
 | 
			
		||||
                return res;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return res;
 | 
			
		||||
@@ -129,7 +155,17 @@ RED.popover = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        create: createPopover
 | 
			
		||||
        create: createPopover,
 | 
			
		||||
        tooltip: function(target,content) {
 | 
			
		||||
            RED.popover.create({
 | 
			
		||||
                target:target,
 | 
			
		||||
                trigger: "hover",
 | 
			
		||||
                size: "small",
 | 
			
		||||
                direction: "bottom",
 | 
			
		||||
                content: content,
 | 
			
		||||
                delay: { show: 550, hide: 10 }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,11 +17,31 @@
 | 
			
		||||
RED.stack = (function() {
 | 
			
		||||
    function createStack(options) {
 | 
			
		||||
        var container = options.container;
 | 
			
		||||
 | 
			
		||||
        container.addClass("red-ui-stack");
 | 
			
		||||
        var contentHeight = 0;
 | 
			
		||||
        var entries = [];
 | 
			
		||||
 | 
			
		||||
        var visible = true;
 | 
			
		||||
        // TODO: make this a singleton function - and watch out for stacks no longer
 | 
			
		||||
        //       in the DOM
 | 
			
		||||
        var resizeStack = function() {
 | 
			
		||||
            if (entries.length > 0) {
 | 
			
		||||
                var headerHeight = 0;
 | 
			
		||||
                entries.forEach(function(entry) {
 | 
			
		||||
                    headerHeight += entry.header.outerHeight();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                var height = container.innerHeight();
 | 
			
		||||
                contentHeight = height - headerHeight - (entries.length-1);
 | 
			
		||||
                entries.forEach(function(e) {
 | 
			
		||||
                    e.contentWrap.height(contentHeight);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (options.fill && options.singleExpanded) {
 | 
			
		||||
            $(window).resize(resizeStack);
 | 
			
		||||
            $(window).focus(resizeStack);
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
            add: function(entry) {
 | 
			
		||||
                entries.push(entry);
 | 
			
		||||
@@ -30,7 +50,12 @@ RED.stack = (function() {
 | 
			
		||||
                    entry.container.hide();
 | 
			
		||||
                }
 | 
			
		||||
                var header = $('<div class="palette-header"></div>').appendTo(entry.container);
 | 
			
		||||
                entry.content = $('<div></div>').appendTo(entry.container);
 | 
			
		||||
                entry.header = header;
 | 
			
		||||
                entry.contentWrap = $('<div></div>',{style:"position:relative"}).appendTo(entry.container);
 | 
			
		||||
                if (options.fill) {
 | 
			
		||||
                    entry.contentWrap.css("height",contentHeight);
 | 
			
		||||
                }
 | 
			
		||||
                entry.content = $('<div></div>').appendTo(entry.contentWrap);
 | 
			
		||||
                if (entry.collapsible !== false) {
 | 
			
		||||
                    header.click(function() {
 | 
			
		||||
                        if (options.singleExpanded) {
 | 
			
		||||
@@ -49,11 +74,13 @@ RED.stack = (function() {
 | 
			
		||||
                    var icon = $('<i class="fa fa-angle-down"></i>').appendTo(header);
 | 
			
		||||
 | 
			
		||||
                    if (entry.expanded) {
 | 
			
		||||
                        entry.container.addClass("palette-category-expanded");
 | 
			
		||||
                        icon.addClass("expanded");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        entry.content.hide();
 | 
			
		||||
                        entry.contentWrap.hide();
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $('<i style="opacity: 0.5;" class="fa fa-angle-down expanded"></i>').appendTo(header);
 | 
			
		||||
                    header.css("cursor","default");
 | 
			
		||||
                }
 | 
			
		||||
                entry.title = $('<span></span>').html(entry.title).appendTo(header);
 | 
			
		||||
@@ -74,24 +101,35 @@ RED.stack = (function() {
 | 
			
		||||
                        if (entry.onexpand) {
 | 
			
		||||
                            entry.onexpand.call(entry);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (options.singleExpanded) {
 | 
			
		||||
                            entries.forEach(function(e) {
 | 
			
		||||
                                if (e !== entry) {
 | 
			
		||||
                                    e.collapse();
 | 
			
		||||
                                }
 | 
			
		||||
                            })
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        icon.addClass("expanded");
 | 
			
		||||
                        entry.content.slideDown(200);
 | 
			
		||||
                        entry.container.addClass("palette-category-expanded");
 | 
			
		||||
                        entry.contentWrap.slideDown(200);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                entry.collapse = function() {
 | 
			
		||||
                    if (entry.isExpanded()) {
 | 
			
		||||
                        icon.removeClass("expanded");
 | 
			
		||||
                        entry.content.slideUp(200);
 | 
			
		||||
                        entry.container.removeClass("palette-category-expanded");
 | 
			
		||||
                        entry.contentWrap.slideUp(200);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                entry.isExpanded = function() {
 | 
			
		||||
                    return icon.hasClass("expanded");
 | 
			
		||||
                    return entry.container.hasClass("palette-category-expanded");
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if (options.fill && options.singleExpanded) {
 | 
			
		||||
                    resizeStack();
 | 
			
		||||
                }
 | 
			
		||||
                return entry;
 | 
			
		||||
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            hide: function() {
 | 
			
		||||
@@ -108,9 +146,13 @@ RED.stack = (function() {
 | 
			
		||||
                    entry.container.show();
 | 
			
		||||
                });
 | 
			
		||||
                return this;
 | 
			
		||||
            },
 | 
			
		||||
            resize: function() {
 | 
			
		||||
                if (resizeStack) {
 | 
			
		||||
                    resizeStack();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,15 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RED.tabs = (function() {
 | 
			
		||||
 | 
			
		||||
    var defaultTabIcon = "fa fa-lemon-o";
 | 
			
		||||
 | 
			
		||||
    function createTabs(options) {
 | 
			
		||||
        var tabs = {};
 | 
			
		||||
        var pinnedTabsCount = 0;
 | 
			
		||||
        var currentTabWidth;
 | 
			
		||||
        var currentActiveTabWidth = 0;
 | 
			
		||||
        var collapsibleMenu;
 | 
			
		||||
 | 
			
		||||
        var ul = options.element || $("#"+options.id);
 | 
			
		||||
        var wrapper = ul.wrap( "<div>" ).parent();
 | 
			
		||||
@@ -50,6 +55,56 @@ RED.tabs = (function() {
 | 
			
		||||
            scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a");
 | 
			
		||||
            scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (options.collapsible) {
 | 
			
		||||
            // var dropDown = $('<div>',{class:"red-ui-tabs-select"}).appendTo(wrapper);
 | 
			
		||||
            // ul.hide();
 | 
			
		||||
            wrapper.addClass("red-ui-tabs-collapsible");
 | 
			
		||||
 | 
			
		||||
            var collapsedButtonsRow = $('<div class="red-ui-tab-link-buttons"></div>').appendTo(wrapper);
 | 
			
		||||
 | 
			
		||||
            var selectButton = $('<a href="#"><i class="fa fa-caret-down"></i></a>').appendTo(collapsedButtonsRow);
 | 
			
		||||
            selectButton.addClass("red-ui-tab-link-button-menu")
 | 
			
		||||
            selectButton.click(function(evt) {
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                if (!collapsibleMenu) {
 | 
			
		||||
                    var pinnedOptions = [];
 | 
			
		||||
                    var options = [];
 | 
			
		||||
                    ul.children().each(function(i,el) {
 | 
			
		||||
                        var id = $(el).data('tabId');
 | 
			
		||||
                        var opt = {
 | 
			
		||||
                            id:"red-ui-tabs-menu-option-"+id,
 | 
			
		||||
                            icon: tabs[id].iconClass || defaultTabIcon,
 | 
			
		||||
                            label: tabs[id].name,
 | 
			
		||||
                            onselect: function() {
 | 
			
		||||
                                activateTab(id);
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        if (tabs[id].pinned) {
 | 
			
		||||
                            pinnedOptions.push(opt);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            options.push(opt);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    options = pinnedOptions.concat(options);
 | 
			
		||||
                    collapsibleMenu = RED.menu.init({id:"debug-message-option-menu",options: options});
 | 
			
		||||
                    collapsibleMenu.css({
 | 
			
		||||
                        position: "absolute"
 | 
			
		||||
                    })
 | 
			
		||||
                    collapsibleMenu.on('mouseleave', function(){ $(this).hide() });
 | 
			
		||||
                    collapsibleMenu.on('mouseup', function() { $(this).hide() });
 | 
			
		||||
                    collapsibleMenu.appendTo("body");
 | 
			
		||||
                }
 | 
			
		||||
                var elementPos = selectButton.offset();
 | 
			
		||||
                collapsibleMenu.css({
 | 
			
		||||
                    top: (elementPos.top+selectButton.height()-20)+"px",
 | 
			
		||||
                    left: (elementPos.left - collapsibleMenu.width() + selectButton.width())+"px"
 | 
			
		||||
                })
 | 
			
		||||
                collapsibleMenu.toggle();
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function scrollEventHandler(evt,dir) {
 | 
			
		||||
            evt.preventDefault();
 | 
			
		||||
            if ($(this).hasClass('disabled')) {
 | 
			
		||||
@@ -118,6 +173,9 @@ RED.tabs = (function() {
 | 
			
		||||
                ul.children().removeClass("active");
 | 
			
		||||
                ul.children().css({"transition": "width 100ms"});
 | 
			
		||||
                link.parent().addClass("active");
 | 
			
		||||
                var parentId = link.parent().attr('id');
 | 
			
		||||
                wrapper.find(".red-ui-tab-link-button").removeClass("active selected");
 | 
			
		||||
                $("#"+parentId+"-link-button").addClass("active selected");
 | 
			
		||||
                if (options.scrollable) {
 | 
			
		||||
                    var pos = link.parent().position().left;
 | 
			
		||||
                    if (pos-21 < 0) {
 | 
			
		||||
@@ -155,41 +213,70 @@ RED.tabs = (function() {
 | 
			
		||||
            var tabs = ul.find("li.red-ui-tab");
 | 
			
		||||
            var width = wrapper.width();
 | 
			
		||||
            var tabCount = tabs.size();
 | 
			
		||||
            var tabWidth = (width-12-(tabCount*6))/tabCount;
 | 
			
		||||
            currentTabWidth = (100*tabWidth/width)+"%";
 | 
			
		||||
            currentActiveTabWidth = currentTabWidth+"%";
 | 
			
		||||
            if (options.scrollable) {
 | 
			
		||||
                tabWidth = Math.max(tabWidth,140);
 | 
			
		||||
                currentTabWidth = tabWidth+"px";
 | 
			
		||||
                currentActiveTabWidth = 0;
 | 
			
		||||
                var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount);
 | 
			
		||||
                ul.width(listWidth);
 | 
			
		||||
                updateScroll();
 | 
			
		||||
            } else if (options.hasOwnProperty("minimumActiveTabWidth")) {
 | 
			
		||||
                if (tabWidth < options.minimumActiveTabWidth) {
 | 
			
		||||
                    tabCount -= 1;
 | 
			
		||||
                    tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount;
 | 
			
		||||
                    currentTabWidth = (100*tabWidth/width)+"%";
 | 
			
		||||
                    currentActiveTabWidth = options.minimumActiveTabWidth+"px";
 | 
			
		||||
            var tabWidth;
 | 
			
		||||
 | 
			
		||||
            if (options.collapsible) {
 | 
			
		||||
                tabWidth = width - collapsedButtonsRow.width()-10;
 | 
			
		||||
                if (tabWidth < 198) {
 | 
			
		||||
                    var delta = 198 - tabWidth;
 | 
			
		||||
                    var b = collapsedButtonsRow.find("a:last").prev();
 | 
			
		||||
                    while (b.is(":not(:visible)")) {
 | 
			
		||||
                        b = b.prev();
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!b.hasClass("red-ui-tab-link-button-pinned")) {
 | 
			
		||||
                        b.hide();
 | 
			
		||||
                    }
 | 
			
		||||
                    tabWidth = width - collapsedButtonsRow.width()-10;
 | 
			
		||||
                } else {
 | 
			
		||||
                    currentActiveTabWidth = 0;
 | 
			
		||||
                    var space = width - 198 - collapsedButtonsRow.width();
 | 
			
		||||
                    if (space > 40) {
 | 
			
		||||
                        collapsedButtonsRow.find("a:not(:visible):first").show();
 | 
			
		||||
                        tabWidth = width - collapsedButtonsRow.width()-10;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            tabs.css({width:currentTabWidth});
 | 
			
		||||
            if (tabWidth < 50) {
 | 
			
		||||
                ul.find(".red-ui-tab-close").hide();
 | 
			
		||||
                ul.find(".red-ui-tab-icon").hide();
 | 
			
		||||
                ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"})
 | 
			
		||||
                tabs.css({width:tabWidth});
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                ul.find(".red-ui-tab-close").show();
 | 
			
		||||
                ul.find(".red-ui-tab-icon").show();
 | 
			
		||||
                ul.find(".red-ui-tab-label").css({paddingLeft:""})
 | 
			
		||||
            }
 | 
			
		||||
            if (currentActiveTabWidth !== 0) {
 | 
			
		||||
                ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
 | 
			
		||||
                ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
 | 
			
		||||
                ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
 | 
			
		||||
                ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""})
 | 
			
		||||
                var tabWidth = (width-12-(tabCount*6))/tabCount;
 | 
			
		||||
                currentTabWidth = (100*tabWidth/width)+"%";
 | 
			
		||||
                currentActiveTabWidth = currentTabWidth+"%";
 | 
			
		||||
                if (options.scrollable) {
 | 
			
		||||
                    tabWidth = Math.max(tabWidth,140);
 | 
			
		||||
                    currentTabWidth = tabWidth+"px";
 | 
			
		||||
                    currentActiveTabWidth = 0;
 | 
			
		||||
                    var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount);
 | 
			
		||||
                    ul.width(listWidth);
 | 
			
		||||
                    updateScroll();
 | 
			
		||||
                } else if (options.hasOwnProperty("minimumActiveTabWidth")) {
 | 
			
		||||
                    if (tabWidth < options.minimumActiveTabWidth) {
 | 
			
		||||
                        tabCount -= 1;
 | 
			
		||||
                        tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount;
 | 
			
		||||
                        currentTabWidth = (100*tabWidth/width)+"%";
 | 
			
		||||
                        currentActiveTabWidth = options.minimumActiveTabWidth+"px";
 | 
			
		||||
                    } else {
 | 
			
		||||
                        currentActiveTabWidth = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (options.collapsible) {
 | 
			
		||||
                    console.log(currentTabWidth);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tabs.css({width:currentTabWidth});
 | 
			
		||||
                if (tabWidth < 50) {
 | 
			
		||||
                    ul.find(".red-ui-tab-close").hide();
 | 
			
		||||
                    ul.find(".red-ui-tab-icon").hide();
 | 
			
		||||
                    ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"})
 | 
			
		||||
                } else {
 | 
			
		||||
                    ul.find(".red-ui-tab-close").show();
 | 
			
		||||
                    ul.find(".red-ui-tab-icon").show();
 | 
			
		||||
                    ul.find(".red-ui-tab-label").css({paddingLeft:""})
 | 
			
		||||
                }
 | 
			
		||||
                if (currentActiveTabWidth !== 0) {
 | 
			
		||||
                    ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
 | 
			
		||||
                    ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
 | 
			
		||||
                    ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
 | 
			
		||||
                    ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""})
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
@@ -210,11 +297,15 @@ RED.tabs = (function() {
 | 
			
		||||
                activateTab(tab.find("a"));
 | 
			
		||||
            }
 | 
			
		||||
            li.remove();
 | 
			
		||||
            if (tabs[id].pinned) {
 | 
			
		||||
                pinnedTabsCount--;
 | 
			
		||||
            }
 | 
			
		||||
            if (options.onremove) {
 | 
			
		||||
                options.onremove(tabs[id]);
 | 
			
		||||
            }
 | 
			
		||||
            delete tabs[id];
 | 
			
		||||
            updateTabWidths();
 | 
			
		||||
            collapsibleMenu = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
@@ -223,13 +314,48 @@ RED.tabs = (function() {
 | 
			
		||||
                var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
 | 
			
		||||
                li.attr('id',"red-ui-tab-"+(tab.id.replace(".","-")));
 | 
			
		||||
                li.data("tabId",tab.id);
 | 
			
		||||
 | 
			
		||||
                if (options.maximumTabWidth) {
 | 
			
		||||
                    li.css("maxWidth",options.maximumTabWidth+"px");
 | 
			
		||||
                }
 | 
			
		||||
                var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
 | 
			
		||||
                if (tab.icon) {
 | 
			
		||||
                    $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
 | 
			
		||||
                } else if (tab.iconClass) {
 | 
			
		||||
                    $('<i>',{class:"red-ui-tab-icon "+tab.iconClass}).appendTo(link);
 | 
			
		||||
                }
 | 
			
		||||
                var span = $('<span/>',{class:"bidiAware"}).text(tab.label).appendTo(link);
 | 
			
		||||
                span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label));
 | 
			
		||||
                if (options.collapsible) {
 | 
			
		||||
                    li.addClass("red-ui-tab-pinned");
 | 
			
		||||
                    var pinnedLink = $('<a href="#'+tab.id+'" class="red-ui-tab-link-button"></a>');
 | 
			
		||||
                    if (tab.pinned) {
 | 
			
		||||
                        if (pinnedTabsCount === 0) {
 | 
			
		||||
                            pinnedLink.prependTo(collapsedButtonsRow)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            pinnedLink.insertAfter(collapsedButtonsRow.find("a.red-ui-tab-link-button-pinned:last"));
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        pinnedLink.insertBefore(collapsedButtonsRow.find("a:last"));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    pinnedLink.attr('id',li.attr('id')+"-link-button");
 | 
			
		||||
                    if (tab.iconClass) {
 | 
			
		||||
                        $('<i>',{class:tab.iconClass}).appendTo(pinnedLink);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $('<i>',{class:defaultTabIcon}).appendTo(pinnedLink);
 | 
			
		||||
                    }
 | 
			
		||||
                    pinnedLink.click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        activateTab(tab.id);
 | 
			
		||||
                    });
 | 
			
		||||
                    if (tab.pinned) {
 | 
			
		||||
                        pinnedLink.addClass("red-ui-tab-link-button-pinned");
 | 
			
		||||
                        pinnedTabsCount++;
 | 
			
		||||
                    }
 | 
			
		||||
                    RED.popover.tooltip($(pinnedLink), tab.name);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                link.on("click",onTabClick);
 | 
			
		||||
                link.on("dblclick",onTabDblClick);
 | 
			
		||||
                if (tab.closeable) {
 | 
			
		||||
@@ -241,7 +367,6 @@ RED.tabs = (function() {
 | 
			
		||||
                        removeTab(tab.id);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                updateTabWidths();
 | 
			
		||||
                if (options.onadd) {
 | 
			
		||||
                    options.onadd(tab);
 | 
			
		||||
                }
 | 
			
		||||
@@ -326,6 +451,10 @@ RED.tabs = (function() {
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
                    updateTabWidths();
 | 
			
		||||
                },10);
 | 
			
		||||
                collapsibleMenu = null;
 | 
			
		||||
            },
 | 
			
		||||
            removeTab: removeTab,
 | 
			
		||||
            activateTab: activateTab,
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,38 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 **/
 | 
			
		||||
(function($) {
 | 
			
		||||
    var contextParse = function(v) {
 | 
			
		||||
        var parts = RED.utils.parseContextKey(v);
 | 
			
		||||
        return {
 | 
			
		||||
            option: parts.store,
 | 
			
		||||
            value: parts.key
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    var contextExport = function(v,opt) {
 | 
			
		||||
        if (!opt) {
 | 
			
		||||
            return v;
 | 
			
		||||
        }
 | 
			
		||||
        var store = ((typeof opt === "string")?opt:opt.value)
 | 
			
		||||
        if (store !== RED.settings.context.default) {
 | 
			
		||||
            return "#:("+store+")::"+v;
 | 
			
		||||
        } else {
 | 
			
		||||
            return v;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    var allOptions = {
 | 
			
		||||
        msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression},
 | 
			
		||||
        flow: {value:"flow",label:"flow.",validate:RED.utils.validatePropertyExpression},
 | 
			
		||||
        global: {value:"global",label:"global.",validate:RED.utils.validatePropertyExpression},
 | 
			
		||||
        flow: {value:"flow",label:"flow.",hasValue:true,
 | 
			
		||||
            options:[],
 | 
			
		||||
            validate:RED.utils.validatePropertyExpression,
 | 
			
		||||
            parse: contextParse,
 | 
			
		||||
            export: contextExport
 | 
			
		||||
        },
 | 
			
		||||
        global: {value:"global",label:"global.",hasValue:true,
 | 
			
		||||
            options:[],
 | 
			
		||||
            validate:RED.utils.validatePropertyExpression,
 | 
			
		||||
            parse: contextParse,
 | 
			
		||||
            export: contextExport
 | 
			
		||||
        },
 | 
			
		||||
        str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"},
 | 
			
		||||
        num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
 | 
			
		||||
        bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]},
 | 
			
		||||
@@ -76,31 +104,51 @@
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        env: {
 | 
			
		||||
            value: "env",
 | 
			
		||||
            label: "env variable",
 | 
			
		||||
            icon: "red/images/typedInput/env.png"
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    var nlsd = false;
 | 
			
		||||
 | 
			
		||||
    $.widget( "nodered.typedInput", {
 | 
			
		||||
        _create: function() {
 | 
			
		||||
            try {
 | 
			
		||||
            if (!nlsd && RED && RED._) {
 | 
			
		||||
                for (var i in allOptions) {
 | 
			
		||||
                    if (allOptions.hasOwnProperty(i)) {
 | 
			
		||||
                        allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label});
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var contextStores = RED.settings.context.stores;
 | 
			
		||||
                var contextOptions = contextStores.map(function(store) {
 | 
			
		||||
                    return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database" style="color: #'+(store==='memory'?'ddd':'777')+'"></i>'}
 | 
			
		||||
                })
 | 
			
		||||
                if (contextOptions.length < 2) {
 | 
			
		||||
                    allOptions.flow.options = [];
 | 
			
		||||
                    allOptions.global.options = [];
 | 
			
		||||
                } else {
 | 
			
		||||
                    allOptions.flow.options = contextOptions;
 | 
			
		||||
                    allOptions.global.options = contextOptions;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            nlsd = true;
 | 
			
		||||
            var that = this;
 | 
			
		||||
 | 
			
		||||
            this.disarmClick = false;
 | 
			
		||||
            this.input = $('<input type="text"></input>');
 | 
			
		||||
            this.input.insertAfter(this.element);
 | 
			
		||||
            this.input.val(this.element.val());
 | 
			
		||||
            this.element.addClass('red-ui-typedInput');
 | 
			
		||||
            this.uiWidth = this.element.outerWidth();
 | 
			
		||||
            this.elementDiv = this.element.wrap("<div>").parent().addClass('red-ui-typedInput-input');
 | 
			
		||||
            this.elementDiv = this.input.wrap("<div>").parent().addClass('red-ui-typedInput-input');
 | 
			
		||||
            this.uiSelect = this.elementDiv.wrap( "<div>" ).parent();
 | 
			
		||||
            var attrStyle = this.element.attr('style');
 | 
			
		||||
            var m;
 | 
			
		||||
            if ((m = /width\s*:\s*(\d+(%|px))/i.exec(attrStyle)) !== null) {
 | 
			
		||||
                this.element.css('width','100%');
 | 
			
		||||
            if ((m = /width\s*:\s*(calc\s*\(.*\)|\d+(%|px))/i.exec(attrStyle)) !== null) {
 | 
			
		||||
                this.input.css('width','100%');
 | 
			
		||||
                this.uiSelect.width(m[1]);
 | 
			
		||||
                this.uiWidth = null;
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -109,15 +157,19 @@
 | 
			
		||||
            ["Right","Left"].forEach(function(d) {
 | 
			
		||||
                var m = that.element.css("margin"+d);
 | 
			
		||||
                that.uiSelect.css("margin"+d,m);
 | 
			
		||||
                that.element.css("margin"+d,0);
 | 
			
		||||
                that.input.css("margin"+d,0);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this.uiSelect.addClass("red-ui-typedInput-container");
 | 
			
		||||
 | 
			
		||||
            this.element.attr('type','hidden');
 | 
			
		||||
 | 
			
		||||
            this.options.types = this.options.types||Object.keys(allOptions);
 | 
			
		||||
 | 
			
		||||
            this.selectTrigger = $('<button tabindex="0"></button>').prependTo(this.uiSelect);
 | 
			
		||||
            $('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
 | 
			
		||||
            this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
 | 
			
		||||
            $('<i class="red-ui-typedInput-icon fa fa-sort-desc"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
 | 
			
		||||
 | 
			
		||||
            this.selectLabel = $('<span class="red-ui-typedInput-type-label"></span>').appendTo(this.selectTrigger);
 | 
			
		||||
 | 
			
		||||
            this.types(this.options.types);
 | 
			
		||||
 | 
			
		||||
@@ -131,14 +183,16 @@
 | 
			
		||||
                this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.element.on('focus', function() {
 | 
			
		||||
            this.input.on('focus', function() {
 | 
			
		||||
                that.uiSelect.addClass('red-ui-typedInput-focus');
 | 
			
		||||
            });
 | 
			
		||||
            this.element.on('blur', function() {
 | 
			
		||||
            this.input.on('blur', function() {
 | 
			
		||||
                that.uiSelect.removeClass('red-ui-typedInput-focus');
 | 
			
		||||
            });
 | 
			
		||||
            this.element.on('change', function() {
 | 
			
		||||
            this.input.on('change', function() {
 | 
			
		||||
                that.validate();
 | 
			
		||||
                that.element.val(that.value());
 | 
			
		||||
                that.element.trigger('change',that.propertyType,that.value());
 | 
			
		||||
            })
 | 
			
		||||
            this.selectTrigger.click(function(event) {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
@@ -154,8 +208,11 @@
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            // explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
 | 
			
		||||
            this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="fa fa-sort-desc"></i></span></button>').appendTo(this.uiSelect);
 | 
			
		||||
            this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="red-ui-typedInput-icon fa fa-sort-desc"></i></span></button>').appendTo(this.uiSelect);
 | 
			
		||||
            this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
 | 
			
		||||
            RED.popover.tooltip(this.optionSelectLabel,function() {
 | 
			
		||||
                return that.optionValue;
 | 
			
		||||
            });
 | 
			
		||||
            this.optionSelectTrigger.click(function(event) {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                that._showOptionSelectMenu();
 | 
			
		||||
@@ -170,17 +227,18 @@
 | 
			
		||||
                that.uiSelect.addClass('red-ui-typedInput-focus');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"><i class="fa fa-ellipsis-h"></i></button>').appendTo(this.uiSelect);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"><i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i></button>').appendTo(this.uiSelect);
 | 
			
		||||
            this.type(this.options.default||this.typeList[0].value);
 | 
			
		||||
        }catch(err) {
 | 
			
		||||
            console.log(err.stack);
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
        _showTypeMenu: function() {
 | 
			
		||||
            if (this.typeList.length > 1) {
 | 
			
		||||
                this._showMenu(this.menu,this.selectTrigger);
 | 
			
		||||
                this.menu.find("[value='"+this.propertyType+"']").focus();
 | 
			
		||||
            } else {
 | 
			
		||||
                this.element.focus();
 | 
			
		||||
                this.input.focus();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        _showOptionSelectMenu: function() {
 | 
			
		||||
@@ -189,8 +247,8 @@
 | 
			
		||||
                    minWidth:this.optionSelectLabel.width()
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                this._showMenu(this.optionMenu,this.optionSelectLabel);
 | 
			
		||||
                var selectedOption = this.optionMenu.find("[value='"+this.value()+"']");
 | 
			
		||||
                this._showMenu(this.optionMenu,this.optionSelectTrigger);
 | 
			
		||||
                var selectedOption = this.optionMenu.find("[value='"+this.optionValue+"']");
 | 
			
		||||
                if (selectedOption.length === 0) {
 | 
			
		||||
                    selectedOption = this.optionMenu.children(":first");
 | 
			
		||||
                }
 | 
			
		||||
@@ -202,7 +260,7 @@
 | 
			
		||||
            $(document).off("mousedown.close-property-select");
 | 
			
		||||
            menu.hide();
 | 
			
		||||
            if (this.elementDiv.is(":visible")) {
 | 
			
		||||
                this.element.focus();
 | 
			
		||||
                this.input.focus();
 | 
			
		||||
            } else if (this.optionSelectTrigger.is(":visible")){
 | 
			
		||||
                this.optionSelectTrigger.focus();
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -221,10 +279,19 @@
 | 
			
		||||
                    op.text(opt.label);
 | 
			
		||||
                }
 | 
			
		||||
                if (opt.icon) {
 | 
			
		||||
                    $('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
 | 
			
		||||
                    if (opt.icon.indexOf("<") === 0) {
 | 
			
		||||
                        $(opt.icon).prependTo(op);
 | 
			
		||||
                    } else if (opt.icon.indexOf("/") !== -1) {
 | 
			
		||||
                        $('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(op);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    op.css({paddingLeft: "18px"});
 | 
			
		||||
                }
 | 
			
		||||
                if (!opt.icon && !opt.label) {
 | 
			
		||||
                    op.text(opt.value);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                op.click(function(event) {
 | 
			
		||||
                    event.preventDefault();
 | 
			
		||||
@@ -303,7 +370,8 @@
 | 
			
		||||
            if (this.uiWidth !== null) {
 | 
			
		||||
                this.uiSelect.width(this.uiWidth);
 | 
			
		||||
            }
 | 
			
		||||
            if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
 | 
			
		||||
            var type = this.typeMap[this.propertyType];
 | 
			
		||||
            if (type && type.hasValue === false) {
 | 
			
		||||
                this.selectTrigger.addClass("red-ui-typedInput-full-width");
 | 
			
		||||
            } else {
 | 
			
		||||
                this.selectTrigger.removeClass("red-ui-typedInput-full-width");
 | 
			
		||||
@@ -313,13 +381,68 @@
 | 
			
		||||
                    this.elementDiv.css('right',"22px");
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.elementDiv.css('right','0');
 | 
			
		||||
                    this.input.css({
 | 
			
		||||
                        'border-top-right-radius': '4px',
 | 
			
		||||
                        'border-bottom-right-radius': '4px'
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // if (this.optionSelectTrigger) {
 | 
			
		||||
                //     this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'});
 | 
			
		||||
                // }
 | 
			
		||||
 | 
			
		||||
                if (this.optionSelectTrigger) {
 | 
			
		||||
                    this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'});
 | 
			
		||||
                    if (type && type.options && type.hasValue === true) {
 | 
			
		||||
                        this.optionSelectLabel.css({'left':'auto'})
 | 
			
		||||
                        var lw = this._getLabelWidth(this.optionSelectLabel);
 | 
			
		||||
                        this.optionSelectTrigger.css({'width':(23+lw)+"px"});
 | 
			
		||||
                        this.elementDiv.css('right',(23+lw)+"px");
 | 
			
		||||
                        this.input.css({
 | 
			
		||||
                            'border-top-right-radius': 0,
 | 
			
		||||
                            'border-bottom-right-radius': 0
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.optionSelectLabel.css({'left':'0'})
 | 
			
		||||
                        this.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
 | 
			
		||||
                        if (!this.optionExpandButton.is(":visible")) {
 | 
			
		||||
                            this.elementDiv.css({'right':0});
 | 
			
		||||
                            this.input.css({
 | 
			
		||||
                                'border-top-right-radius': '4px',
 | 
			
		||||
                                'border-bottom-right-radius': '4px'
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        _updateOptionSelectLabel: function(o) {
 | 
			
		||||
            var opt = this.typeMap[this.propertyType];
 | 
			
		||||
            this.optionSelectLabel.empty();
 | 
			
		||||
            if (o.icon) {
 | 
			
		||||
                if (o.icon.indexOf("<") === 0) {
 | 
			
		||||
                    $(o.icon).prependTo(this.optionSelectLabel);
 | 
			
		||||
                } else if (o.icon.indexOf("/") !== -1) {
 | 
			
		||||
                    // url
 | 
			
		||||
                    $('<img>',{src:o.icon,style:"height: 18px;"}).prependTo(this.optionSelectLabel);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // icon class
 | 
			
		||||
                    $('<i>',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (o.label) {
 | 
			
		||||
                this.optionSelectLabel.text(o.label);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.optionSelectLabel.text(o.value);
 | 
			
		||||
            }
 | 
			
		||||
            if (opt.hasValue) {
 | 
			
		||||
                this.optionValue = o.value;
 | 
			
		||||
                this._resize();
 | 
			
		||||
                this.input.trigger('change',this.propertyType,this.value());
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        _destroy: function() {
 | 
			
		||||
            if (this.optionMenu) {
 | 
			
		||||
                this.optionMenu.remove();
 | 
			
		||||
            }
 | 
			
		||||
            this.menu.remove();
 | 
			
		||||
        },
 | 
			
		||||
        types: function(types) {
 | 
			
		||||
@@ -337,13 +460,18 @@
 | 
			
		||||
                return result;
 | 
			
		||||
            });
 | 
			
		||||
            this.selectTrigger.toggleClass("disabled", this.typeList.length === 1);
 | 
			
		||||
            this.selectTrigger.find(".fa-sort-desc").toggle(this.typeList.length > 1)
 | 
			
		||||
            if (this.menu) {
 | 
			
		||||
                this.menu.remove();
 | 
			
		||||
            }
 | 
			
		||||
            this.menu = this._createMenu(this.typeList, function(v) { that.type(v) });
 | 
			
		||||
            if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
 | 
			
		||||
                this.type(this.typeList[0].value);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.propertyType = null;
 | 
			
		||||
                this.type(currentType);
 | 
			
		||||
            }
 | 
			
		||||
            setTimeout(function() {that._resize();},0);
 | 
			
		||||
        },
 | 
			
		||||
        width: function(desiredWidth) {
 | 
			
		||||
            this.uiWidth = desiredWidth;
 | 
			
		||||
@@ -351,16 +479,33 @@
 | 
			
		||||
        },
 | 
			
		||||
        value: function(value) {
 | 
			
		||||
            if (!arguments.length) {
 | 
			
		||||
                return this.element.val();
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this.typeMap[this.propertyType].options) {
 | 
			
		||||
                    if (this.typeMap[this.propertyType].options.indexOf(value) === -1) {
 | 
			
		||||
                        value = "";
 | 
			
		||||
                    }
 | 
			
		||||
                    this.optionSelectLabel.text(value);
 | 
			
		||||
                var v = this.input.val();
 | 
			
		||||
                if (this.typeMap[this.propertyType].export) {
 | 
			
		||||
                    v = this.typeMap[this.propertyType].export(v,this.optionValue)
 | 
			
		||||
                }
 | 
			
		||||
                this.element.val(value);
 | 
			
		||||
                this.element.trigger('change',this.type(),value);
 | 
			
		||||
                return v;
 | 
			
		||||
            } else {
 | 
			
		||||
                var selectedOption;
 | 
			
		||||
                if (this.typeMap[this.propertyType].options) {
 | 
			
		||||
                    for (var i=0;i<this.typeMap[this.propertyType].options.length;i++) {
 | 
			
		||||
                        var op = this.typeMap[this.propertyType].options[i];
 | 
			
		||||
                        if (typeof op === "string") {
 | 
			
		||||
                            if (op === value) {
 | 
			
		||||
                                selectedOption = this.activeOptions[op];
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (op.value === value) {
 | 
			
		||||
                            selectedOption = op;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!selectedOption) {
 | 
			
		||||
                        selectedOption = {value:""}
 | 
			
		||||
                    }
 | 
			
		||||
                    this._updateOptionSelectLabel(selectedOption)
 | 
			
		||||
                }
 | 
			
		||||
                this.input.val(value);
 | 
			
		||||
                this.input.trigger('change',this.type(),value);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        type: function(type) {
 | 
			
		||||
@@ -371,7 +516,9 @@
 | 
			
		||||
                var opt = this.typeMap[type];
 | 
			
		||||
                if (opt && this.propertyType !== type) {
 | 
			
		||||
                    this.propertyType = type;
 | 
			
		||||
                    this.typeField.val(type);
 | 
			
		||||
                    if (this.typeField) {
 | 
			
		||||
                        this.typeField.val(type);
 | 
			
		||||
                    }
 | 
			
		||||
                    this.selectLabel.empty();
 | 
			
		||||
                    var image;
 | 
			
		||||
                    if (opt.icon) {
 | 
			
		||||
@@ -388,16 +535,87 @@
 | 
			
		||||
                        }
 | 
			
		||||
                        if (this.optionSelectTrigger) {
 | 
			
		||||
                            this.optionSelectTrigger.show();
 | 
			
		||||
                            this.elementDiv.hide();
 | 
			
		||||
                            this.optionMenu = this._createMenu(opt.options,function(v){
 | 
			
		||||
                                that.optionSelectLabel.text(v);
 | 
			
		||||
                                that.value(v);
 | 
			
		||||
                            });
 | 
			
		||||
                            var currentVal = this.element.val();
 | 
			
		||||
                            if (opt.options.indexOf(currentVal) !== -1) {
 | 
			
		||||
                                this.optionSelectLabel.text(currentVal);
 | 
			
		||||
                            if (!opt.hasValue) {
 | 
			
		||||
                                this.elementDiv.hide();
 | 
			
		||||
                            } else {
 | 
			
		||||
                                this.value(opt.options[0]);
 | 
			
		||||
                                this.elementDiv.show();
 | 
			
		||||
                            }
 | 
			
		||||
                            this.activeOptions = {};
 | 
			
		||||
                            opt.options.forEach(function(o) {
 | 
			
		||||
                                if (typeof o === 'string') {
 | 
			
		||||
                                    that.activeOptions[o] = {label:o,value:o};
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    that.activeOptions[o.value] = o;
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
 | 
			
		||||
                            if (!that.activeOptions.hasOwnProperty(that.optionValue)) {
 | 
			
		||||
                                that.optionValue = null;
 | 
			
		||||
                            }
 | 
			
		||||
                            this.optionMenu = this._createMenu(opt.options,function(v){
 | 
			
		||||
                                that._updateOptionSelectLabel(that.activeOptions[v]);
 | 
			
		||||
                                if (!opt.hasValue) {
 | 
			
		||||
                                    that.value(that.activeOptions[v].value)
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                            var op;
 | 
			
		||||
                            if (!opt.hasValue) {
 | 
			
		||||
                                var currentVal = this.input.val();
 | 
			
		||||
                                var validValue = false;
 | 
			
		||||
                                for (var i=0;i<opt.options.length;i++) {
 | 
			
		||||
                                    op = opt.options[i];
 | 
			
		||||
                                    if (typeof op === "string" && op === currentVal) {
 | 
			
		||||
                                        that._updateOptionSelectLabel({value:currentVal});
 | 
			
		||||
                                        validValue = true;
 | 
			
		||||
                                        break;
 | 
			
		||||
                                    } else if (op.value === currentVal) {
 | 
			
		||||
                                        that._updateOptionSelectLabel(op);
 | 
			
		||||
                                        validValue = true;
 | 
			
		||||
                                        break;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                if (!validValue) {
 | 
			
		||||
                                    op = opt.options[0];
 | 
			
		||||
                                    if (typeof op === "string") {
 | 
			
		||||
                                        this.value(op);
 | 
			
		||||
                                        that._updateOptionSelectLabel({value:op});
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        this.value(op.value);
 | 
			
		||||
                                        that._updateOptionSelectLabel(op);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                var selectedOption = this.optionValue||opt.options[0];
 | 
			
		||||
                                if (opt.parse) {
 | 
			
		||||
                                    var parts = opt.parse(this.input.val());
 | 
			
		||||
                                    if (parts.option) {
 | 
			
		||||
                                        selectedOption = parts.option;
 | 
			
		||||
                                        if (!this.activeOptions.hasOwnProperty(selectedOption)) {
 | 
			
		||||
                                            parts.option = Object.keys(this.activeOptions)[0];
 | 
			
		||||
                                            selectedOption = parts.option
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    this.input.val(parts.value);
 | 
			
		||||
                                    if (opt.export) {
 | 
			
		||||
                                        this.element.val(opt.export(parts.value,parts.option||selectedOption));
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                if (typeof selectedOption === "string") {
 | 
			
		||||
                                    this.optionValue = selectedOption;
 | 
			
		||||
                                    if (!this.activeOptions.hasOwnProperty(selectedOption)) {
 | 
			
		||||
                                        selectedOption = Object.keys(this.activeOptions)[0];
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if (!selectedOption) {
 | 
			
		||||
                                        this.optionSelectTrigger.hide();
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        this._updateOptionSelectLabel(this.activeOptions[selectedOption]);
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else if (selectedOption) {
 | 
			
		||||
                                    this.optionValue = selectedOption.value;
 | 
			
		||||
                                    this._updateOptionSelectLabel(selectedOption);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    this.optionSelectTrigger.hide();
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
@@ -409,27 +627,29 @@
 | 
			
		||||
                            this.optionSelectTrigger.hide();
 | 
			
		||||
                        }
 | 
			
		||||
                        if (opt.hasValue === false) {
 | 
			
		||||
                            this.oldValue = this.element.val();
 | 
			
		||||
                            this.element.val("");
 | 
			
		||||
                            this.oldValue = this.input.val();
 | 
			
		||||
                            this.input.val("");
 | 
			
		||||
                            this.elementDiv.hide();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (this.oldValue !== undefined) {
 | 
			
		||||
                                this.element.val(this.oldValue);
 | 
			
		||||
                                this.input.val(this.oldValue);
 | 
			
		||||
                                delete this.oldValue;
 | 
			
		||||
                            }
 | 
			
		||||
                            this.elementDiv.show();
 | 
			
		||||
                        }
 | 
			
		||||
                        if (opt.expand && typeof opt.expand === 'function') {
 | 
			
		||||
                            this.optionExpandButton.show();
 | 
			
		||||
                            this.optionExpandButton.off('click');
 | 
			
		||||
                            this.optionExpandButton.on('click',function(evt) {
 | 
			
		||||
                                evt.preventDefault();
 | 
			
		||||
                                opt.expand.call(that);
 | 
			
		||||
                            })
 | 
			
		||||
                        } else {
 | 
			
		||||
                            this.optionExpandButton.hide();
 | 
			
		||||
                        if (this.optionExpandButton) {
 | 
			
		||||
                            if (opt.expand && typeof opt.expand === 'function') {
 | 
			
		||||
                                this.optionExpandButton.show();
 | 
			
		||||
                                this.optionExpandButton.off('click');
 | 
			
		||||
                                this.optionExpandButton.on('click',function(evt) {
 | 
			
		||||
                                    evt.preventDefault();
 | 
			
		||||
                                    opt.expand.call(that);
 | 
			
		||||
                                })
 | 
			
		||||
                            } else {
 | 
			
		||||
                                this.optionExpandButton.hide();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        this.element.trigger('change',this.propertyType,this.value());
 | 
			
		||||
                        this.input.trigger('change',this.propertyType,this.value());
 | 
			
		||||
                    }
 | 
			
		||||
                    if (image) {
 | 
			
		||||
                        image.onload = function() { that._resize(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -97,113 +97,6 @@ RED.deploy = (function() {
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:deploy-flows",save);
 | 
			
		||||
 | 
			
		||||
        $( "#node-dialog-confirm-deploy" ).dialog({
 | 
			
		||||
                title: RED._('deploy.confirm.button.confirm'),
 | 
			
		||||
                modal: true,
 | 
			
		||||
                autoOpen: false,
 | 
			
		||||
                width: 550,
 | 
			
		||||
                height: "auto",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-confirm-deploy-review",
 | 
			
		||||
                        text: RED._("deploy.confirm.button.review"),
 | 
			
		||||
                        class: "primary disabled",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            if (!$("#node-dialog-confirm-deploy-review").hasClass('disabled')) {
 | 
			
		||||
                                RED.diff.showRemoteDiff();
 | 
			
		||||
                                $( this ).dialog( "close" );
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-confirm-deploy-merge",
 | 
			
		||||
                        text: RED._("deploy.confirm.button.merge"),
 | 
			
		||||
                        class: "primary disabled",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.diff.mergeDiff(currentDiff);
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-confirm-deploy-deploy",
 | 
			
		||||
                        text: RED._("deploy.confirm.button.confirm"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
 | 
			
		||||
                            var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked");
 | 
			
		||||
                            if (ignoreChecked) {
 | 
			
		||||
                                ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true;
 | 
			
		||||
                            }
 | 
			
		||||
                            save(true,/conflict/.test($("#node-dialog-confirm-deploy-type" ).val()));
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-confirm-deploy-overwrite",
 | 
			
		||||
                        text: RED._("deploy.confirm.button.overwrite"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            save(true,/conflict/.test($("#node-dialog-confirm-deploy-type" ).val()));
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                create: function() {
 | 
			
		||||
                    $("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
 | 
			
		||||
                        .prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
 | 
			
		||||
                                   '<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
 | 
			
		||||
                                   '<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
 | 
			
		||||
                                   '<input type="hidden" id="node-dialog-confirm-deploy-type">'+
 | 
			
		||||
                                   '</div>');
 | 
			
		||||
                },
 | 
			
		||||
                open: function() {
 | 
			
		||||
                    var deployType = $("#node-dialog-confirm-deploy-type" ).val();
 | 
			
		||||
                    if (/conflict/.test(deployType)) {
 | 
			
		||||
                        $( "#node-dialog-confirm-deploy" ).dialog('option','title', RED._('deploy.confirm.button.review'));
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-deploy").hide();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-review").addClass('disabled').show();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-merge").addClass('disabled').show();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-overwrite").toggle(deployType === "deploy-conflict");
 | 
			
		||||
                        currentDiff = null;
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-conflict-checking").show();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-conflict-auto-merge").hide();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-conflict-manual-merge").hide();
 | 
			
		||||
 | 
			
		||||
                        var now = Date.now();
 | 
			
		||||
                        RED.diff.getRemoteDiff(function(diff) {
 | 
			
		||||
                            var ellapsed = Math.max(1000 - (Date.now()-now), 0);
 | 
			
		||||
                            currentDiff = diff;
 | 
			
		||||
                            setTimeout(function() {
 | 
			
		||||
                                $("#node-dialog-confirm-deploy-conflict-checking").hide();
 | 
			
		||||
                                var d = Object.keys(diff.conflicts);
 | 
			
		||||
                                if (d.length === 0) {
 | 
			
		||||
                                    $("#node-dialog-confirm-deploy-conflict-auto-merge").show();
 | 
			
		||||
                                    $("#node-dialog-confirm-deploy-merge").removeClass('disabled')
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    $("#node-dialog-confirm-deploy-conflict-manual-merge").show();
 | 
			
		||||
                                }
 | 
			
		||||
                                $("#node-dialog-confirm-deploy-review").removeClass('disabled')
 | 
			
		||||
                            },ellapsed);
 | 
			
		||||
                        })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-hide").parent().hide();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $( "#node-dialog-confirm-deploy" ).dialog('option','title', RED._('deploy.confirm.button.confirm'));
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-deploy").show();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-overwrite").hide();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-review").hide();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-merge").hide();
 | 
			
		||||
                        $("#node-dialog-confirm-deploy-hide").parent().show();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.events.on('nodes:change',function(state) {
 | 
			
		||||
            if (state.dirty) {
 | 
			
		||||
@@ -224,24 +117,30 @@ RED.deploy = (function() {
 | 
			
		||||
                if (currentRev === null || deployInflight || currentRev === msg.revision) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                var message = $('<div>'+RED._('deploy.confirm.backgroundUpdate')+
 | 
			
		||||
                    '<br><br><div class="ui-dialog-buttonset">'+
 | 
			
		||||
                    '<button>'+RED._('deploy.confirm.button.ignore')+'</button>'+
 | 
			
		||||
                    '<button class="primary">'+RED._('deploy.confirm.button.review')+'</button>'+
 | 
			
		||||
                    '</div></div>');
 | 
			
		||||
                $(message.find('button')[0]).click(function(evt) {
 | 
			
		||||
                    evt.preventDefault();
 | 
			
		||||
                    activeNotifyMessage.close();
 | 
			
		||||
                    activeNotifyMessage = null;
 | 
			
		||||
                })
 | 
			
		||||
                $(message.find('button')[1]).click(function(evt) {
 | 
			
		||||
                    evt.preventDefault();
 | 
			
		||||
                    activeNotifyMessage.close();
 | 
			
		||||
                    var nns = RED.nodes.createCompleteNodeSet();
 | 
			
		||||
                    resolveConflict(nns,false);
 | 
			
		||||
                    activeNotifyMessage = null;
 | 
			
		||||
                })
 | 
			
		||||
                activeNotifyMessage = RED.notify(message,null,true);
 | 
			
		||||
                var message = $('<p>').text(RED._('deploy.confirm.backgroundUpdate'));
 | 
			
		||||
                activeNotifyMessage = RED.notify(message,{
 | 
			
		||||
                    modal: true,
 | 
			
		||||
                    fixed: true,
 | 
			
		||||
                    buttons: [
 | 
			
		||||
                        {
 | 
			
		||||
                            text: RED._('deploy.confirm.button.ignore'),
 | 
			
		||||
                            click: function() {
 | 
			
		||||
                                activeNotifyMessage.close();
 | 
			
		||||
                                activeNotifyMessage = null;
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            text: RED._('deploy.confirm.button.review'),
 | 
			
		||||
                            class: "primary",
 | 
			
		||||
                            click: function() {
 | 
			
		||||
                                activeNotifyMessage.close();
 | 
			
		||||
                                var nns = RED.nodes.createCompleteNodeSet();
 | 
			
		||||
                                resolveConflict(nns,false);
 | 
			
		||||
                                activeNotifyMessage = null;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -271,16 +170,99 @@ RED.deploy = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function resolveConflict(currentNodes, activeDeploy) {
 | 
			
		||||
        $( "#node-dialog-confirm-deploy-config" ).hide();
 | 
			
		||||
        $( "#node-dialog-confirm-deploy-unknown" ).hide();
 | 
			
		||||
        $( "#node-dialog-confirm-deploy-unused" ).hide();
 | 
			
		||||
        $( "#node-dialog-confirm-deploy-conflict" ).show();
 | 
			
		||||
        $( "#node-dialog-confirm-deploy-type" ).val(activeDeploy?"deploy-conflict":"background-conflict");
 | 
			
		||||
        $( "#node-dialog-confirm-deploy" ).dialog( "open" );
 | 
			
		||||
    }
 | 
			
		||||
        var message = $('<div>');
 | 
			
		||||
        $('<p data-i18n="deploy.confirm.conflict"></p>').appendTo(message);
 | 
			
		||||
        var conflictCheck = $('<div id="node-dialog-confirm-deploy-conflict-checking" class="node-dialog-confirm-conflict-row">'+
 | 
			
		||||
            '<img src="red/images/spin.svg"/><div data-i18n="deploy.confirm.conflictChecking"></div>'+
 | 
			
		||||
        '</div>').appendTo(message);
 | 
			
		||||
        var conflictAutoMerge = $('<div class="node-dialog-confirm-conflict-row">'+
 | 
			
		||||
            '<i style="color: #3a3;" class="fa fa-check"></i><div data-i18n="deploy.confirm.conflictAutoMerge"></div>'+
 | 
			
		||||
            '</div>').hide().appendTo(message);
 | 
			
		||||
        var conflictManualMerge = $('<div id="node-dialog-confirm-deploy-conflict-manual-merge" class="node-dialog-confirm-conflict-row">'+
 | 
			
		||||
            '<i style="color: #999;" class="fa fa-exclamation"></i><div data-i18n="deploy.confirm.conflictManualMerge"></div>'+
 | 
			
		||||
            '</div>').hide().appendTo(message);
 | 
			
		||||
 | 
			
		||||
        message.i18n();
 | 
			
		||||
        currentDiff = null;
 | 
			
		||||
        var buttons = [
 | 
			
		||||
            {
 | 
			
		||||
                text: RED._("common.label.cancel"),
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    conflictNotification.close();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id: "node-dialog-confirm-deploy-review",
 | 
			
		||||
                text: RED._("deploy.confirm.button.review"),
 | 
			
		||||
                class: "primary disabled",
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    if (!$("#node-dialog-confirm-deploy-review").hasClass('disabled')) {
 | 
			
		||||
                        RED.diff.showRemoteDiff();
 | 
			
		||||
                        conflictNotification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id: "node-dialog-confirm-deploy-merge",
 | 
			
		||||
                text: RED._("deploy.confirm.button.merge"),
 | 
			
		||||
                class: "primary disabled",
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    if (!$("#node-dialog-confirm-deploy-merge").hasClass('disabled')) {
 | 
			
		||||
                        RED.diff.mergeDiff(currentDiff);
 | 
			
		||||
                        conflictNotification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
        if (activeDeploy) {
 | 
			
		||||
            buttons.push({
 | 
			
		||||
                id: "node-dialog-confirm-deploy-overwrite",
 | 
			
		||||
                text: RED._("deploy.confirm.button.overwrite"),
 | 
			
		||||
                class: "primary",
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    save(true,activeDeploy);
 | 
			
		||||
                    conflictNotification.close();
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        var conflictNotification = RED.notify(message,{
 | 
			
		||||
            modal: true,
 | 
			
		||||
            fixed: true,
 | 
			
		||||
            width: 600,
 | 
			
		||||
            buttons: buttons
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        var now = Date.now();
 | 
			
		||||
        RED.diff.getRemoteDiff(function(diff) {
 | 
			
		||||
            var ellapsed = Math.max(1000 - (Date.now()-now), 0);
 | 
			
		||||
            currentDiff = diff;
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                conflictCheck.hide();
 | 
			
		||||
                var d = Object.keys(diff.conflicts);
 | 
			
		||||
                if (d.length === 0) {
 | 
			
		||||
                    conflictAutoMerge.show();
 | 
			
		||||
                    $("#node-dialog-confirm-deploy-merge").removeClass('disabled')
 | 
			
		||||
                } else {
 | 
			
		||||
                    conflictManualMerge.show();
 | 
			
		||||
                }
 | 
			
		||||
                $("#node-dialog-confirm-deploy-review").removeClass('disabled')
 | 
			
		||||
            },ellapsed);
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    function cropList(list) {
 | 
			
		||||
        if (list.length > 5) {
 | 
			
		||||
            var remainder = list.length - 5;
 | 
			
		||||
            list = list.slice(0,5);
 | 
			
		||||
            list.push(RED._("deploy.confirm.plusNMore",{count:remainder}));
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
    function save(skipValidation,force) {
 | 
			
		||||
        if (!$("#btn-deploy").hasClass("disabled")) {
 | 
			
		||||
            if (!RED.user.hasPermission("flows.write")) {
 | 
			
		||||
                RED.notify(RED._("user.errors.deploy"),"error");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!skipValidation) {
 | 
			
		||||
                var hasUnknown = false;
 | 
			
		||||
                var hasInvalid = false;
 | 
			
		||||
@@ -310,39 +292,62 @@ RED.deploy = (function() {
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                $( "#node-dialog-confirm-deploy-config" ).hide();
 | 
			
		||||
                $( "#node-dialog-confirm-deploy-unknown" ).hide();
 | 
			
		||||
                $( "#node-dialog-confirm-deploy-unused" ).hide();
 | 
			
		||||
                $( "#node-dialog-confirm-deploy-conflict" ).hide();
 | 
			
		||||
 | 
			
		||||
                var showWarning = false;
 | 
			
		||||
 | 
			
		||||
                var notificationMessage;
 | 
			
		||||
                var notificationButtons = [];
 | 
			
		||||
                var notification;
 | 
			
		||||
                if (hasUnknown && !ignoreDeployWarnings.unknown) {
 | 
			
		||||
                    showWarning = true;
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-type" ).val("unknown");
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-unknown" ).show();
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-unknown-list" )
 | 
			
		||||
                        .html("<li>"+unknownNodes.join("</li><li>")+"</li>");
 | 
			
		||||
                    notificationMessage = "<p>"+RED._('deploy.confirm.unknown')+"</p>"+
 | 
			
		||||
                        '<ul class="node-dialog-configm-deploy-list"><li>'+cropList(unknownNodes).join("</li><li>")+"</li></ul><p>"+
 | 
			
		||||
                        RED._('deploy.confirm.confirm')+
 | 
			
		||||
                        "</p>";
 | 
			
		||||
 | 
			
		||||
                    notificationButtons= [
 | 
			
		||||
                        {
 | 
			
		||||
                            id: "node-dialog-confirm-deploy-deploy",
 | 
			
		||||
                            text: RED._("deploy.confirm.button.confirm"),
 | 
			
		||||
                            class: "primary",
 | 
			
		||||
                            click: function() {
 | 
			
		||||
                                save(true);
 | 
			
		||||
                                notification.close();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ];
 | 
			
		||||
                } else if (hasInvalid && !ignoreDeployWarnings.invalid) {
 | 
			
		||||
                    showWarning = true;
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-type" ).val("invalid");
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-config" ).show();
 | 
			
		||||
                    invalidNodes.sort(sortNodeInfo);
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-invalid-list" )
 | 
			
		||||
                        .html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
 | 
			
		||||
 | 
			
		||||
                } else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
 | 
			
		||||
                    // showWarning = true;
 | 
			
		||||
                    // $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
 | 
			
		||||
                    // $( "#node-dialog-confirm-deploy-unused" ).show();
 | 
			
		||||
                    //
 | 
			
		||||
                    // unusedConfigNodes.sort(sortNodeInfo);
 | 
			
		||||
                    // $( "#node-dialog-confirm-deploy-unused-list" )
 | 
			
		||||
                    //     .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
 | 
			
		||||
                    notificationMessage = "<p>"+RED._('deploy.confirm.improperlyConfigured')+"</p>"+
 | 
			
		||||
                        '<ul class="node-dialog-configm-deploy-list"><li>'+cropList(invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"})).join("</li><li>")+"</li></ul><p>"+
 | 
			
		||||
                        RED._('deploy.confirm.confirm')+
 | 
			
		||||
                        "</p>";
 | 
			
		||||
                    notificationButtons= [
 | 
			
		||||
                        {
 | 
			
		||||
                            id: "node-dialog-confirm-deploy-deploy",
 | 
			
		||||
                            text: RED._("deploy.confirm.button.confirm"),
 | 
			
		||||
                            class: "primary",
 | 
			
		||||
                            click: function() {
 | 
			
		||||
                                save(true);
 | 
			
		||||
                                notification.close();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
                if (showWarning) {
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy-hide" ).prop("checked",false);
 | 
			
		||||
                    $( "#node-dialog-confirm-deploy" ).dialog( "open" );
 | 
			
		||||
                    notificationButtons.unshift(
 | 
			
		||||
                        {
 | 
			
		||||
                            text: RED._("common.label.cancel"),
 | 
			
		||||
                            click: function() {
 | 
			
		||||
                                notification.close();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                    notification = RED.notify(notificationMessage,{
 | 
			
		||||
                        modal: true,
 | 
			
		||||
                        fixed: true,
 | 
			
		||||
                        buttons:notificationButtons
 | 
			
		||||
                    });
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -361,6 +366,10 @@ RED.deploy = (function() {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            deployInflight = true;
 | 
			
		||||
            $("#header-shade").show();
 | 
			
		||||
            $("#editor-shade").show();
 | 
			
		||||
            $("#palette-shade").show();
 | 
			
		||||
            $("#sidebar-shade").show();
 | 
			
		||||
            $.ajax({
 | 
			
		||||
                url:"flows",
 | 
			
		||||
                type: "POST",
 | 
			
		||||
@@ -378,7 +387,7 @@ RED.deploy = (function() {
 | 
			
		||||
                    '<p>'+RED._("deploy.successfulDeploy")+'</p>'+
 | 
			
		||||
                    '<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.notify(RED._("deploy.successfulDeploy"),"success");
 | 
			
		||||
                    RED.notify('<p>'+RED._("deploy.successfulDeploy")+'</p>',"success");
 | 
			
		||||
                }
 | 
			
		||||
                RED.nodes.eachNode(function(node) {
 | 
			
		||||
                    if (node.changed) {
 | 
			
		||||
@@ -399,9 +408,12 @@ RED.deploy = (function() {
 | 
			
		||||
                        delete confNode.credentials;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                RED.nodes.eachSubflow(function(subflow) {
 | 
			
		||||
                    subflow.changed = false;
 | 
			
		||||
                });
 | 
			
		||||
                RED.nodes.eachWorkspace(function(ws) {
 | 
			
		||||
                    ws.changed = false;
 | 
			
		||||
                })
 | 
			
		||||
                });
 | 
			
		||||
                // Once deployed, cannot undo back to a clean state
 | 
			
		||||
                RED.history.markAllDirty();
 | 
			
		||||
                RED.view.redraw();
 | 
			
		||||
@@ -424,11 +436,19 @@ RED.deploy = (function() {
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
                    $(".deploy-button-content").css('opacity',1);
 | 
			
		||||
                    $(".deploy-button-spinner").hide();
 | 
			
		||||
                    $("#header-shade").hide();
 | 
			
		||||
                    $("#editor-shade").hide();
 | 
			
		||||
                    $("#palette-shade").hide();
 | 
			
		||||
                    $("#sidebar-shade").hide();
 | 
			
		||||
                },delta);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
        init: init
 | 
			
		||||
        init: init,
 | 
			
		||||
        setDeployInflight: function(state) {
 | 
			
		||||
            deployInflight = state;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1438
									
								
								editor/js/ui/diff.js
									
									
									
									
									
								
							
							
						
						
									
										1438
									
								
								editor/js/ui/diff.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										209
									
								
								editor/js/ui/editors/buffer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								editor/js/ui/editors/buffer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,209 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.editor.types._buffer = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var template = '<script type="text/x-red" data-template-name="_buffer"><div id="node-input-buffer-panels"><div id="node-input-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><span class="node-input-buffer-type"><i class="fa fa-exclamation-circle"></i> <span id="node-input-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="node-input-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></span></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="node-input-buffer-str"></div></div></div><div id="node-input-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px"><div class="node-text-editor" id="node-input-buffer-bin"></div></div></div></div></script>';
 | 
			
		||||
 | 
			
		||||
    function stringToUTF8Array(str) {
 | 
			
		||||
        var data = [];
 | 
			
		||||
        var i=0, l = str.length;
 | 
			
		||||
        for (i=0; i<l; i++) {
 | 
			
		||||
            var char = str.charCodeAt(i);
 | 
			
		||||
            if (char < 0x80) {
 | 
			
		||||
                data.push(char);
 | 
			
		||||
            } else if (char < 0x800) {
 | 
			
		||||
                data.push(0xc0 | (char >> 6));
 | 
			
		||||
                data.push(0x80 | (char & 0x3f));
 | 
			
		||||
            } else if (char < 0xd800 || char >= 0xe000) {
 | 
			
		||||
                data.push(0xe0 | (char >> 12));
 | 
			
		||||
                data.push(0x80 | ((char>>6) & 0x3f));
 | 
			
		||||
                data.push(0x80 | (char & 0x3f));
 | 
			
		||||
            } else {
 | 
			
		||||
                i++;
 | 
			
		||||
                char = 0x10000 + (((char & 0x3ff)<<10) | (str.charAt(i) & 0x3ff));
 | 
			
		||||
                data.push(0xf0 | (char >>18));
 | 
			
		||||
                data.push(0x80 | ((char>>12) & 0x3f));
 | 
			
		||||
                data.push(0x80 | ((char>>6) & 0x3f));
 | 
			
		||||
                data.push(0x80 | (char & 0x3f));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            $(template).appendTo(document.body);
 | 
			
		||||
        },
 | 
			
		||||
        show: function(options) {
 | 
			
		||||
            var value = options.value;
 | 
			
		||||
            var onComplete = options.complete;
 | 
			
		||||
            var type = "_buffer"
 | 
			
		||||
            RED.view.state(RED.state.EDITING);
 | 
			
		||||
            var bufferStringEditor = [];
 | 
			
		||||
            var bufferBinValue;
 | 
			
		||||
 | 
			
		||||
            var panels;
 | 
			
		||||
 | 
			
		||||
            var trayOptions = {
 | 
			
		||||
                title: options.title,
 | 
			
		||||
                width: "inherit",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-cancel",
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-ok",
 | 
			
		||||
                        text: RED._("common.label.done"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            onComplete(JSON.stringify(bufferBinValue));
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                resize: function(dimensions) {
 | 
			
		||||
                    var height = $("#dialog-form").height();
 | 
			
		||||
                    if (panels) {
 | 
			
		||||
                        panels.resize(height);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                open: function(tray) {
 | 
			
		||||
                    var trayBody = tray.find('.editor-tray-body');
 | 
			
		||||
                    var dialogForm = RED.editor.buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
 | 
			
		||||
 | 
			
		||||
                    bufferStringEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-buffer-str',
 | 
			
		||||
                        value: "",
 | 
			
		||||
                        mode:"ace/mode/text"
 | 
			
		||||
                    });
 | 
			
		||||
                    bufferStringEditor.getSession().setValue(value||"",-1);
 | 
			
		||||
 | 
			
		||||
                    bufferBinEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-buffer-bin',
 | 
			
		||||
                        value: "",
 | 
			
		||||
                        mode:"ace/mode/text",
 | 
			
		||||
                        readOnly: true
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    var changeTimer;
 | 
			
		||||
                    var buildBuffer = function(data) {
 | 
			
		||||
                        var valid = true;
 | 
			
		||||
                        var isString = typeof data === 'string';
 | 
			
		||||
                        var binBuffer = [];
 | 
			
		||||
                        if (isString) {
 | 
			
		||||
                            bufferBinValue = stringToUTF8Array(data);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            bufferBinValue = data;
 | 
			
		||||
                        }
 | 
			
		||||
                        var i=0,l=bufferBinValue.length;
 | 
			
		||||
                        var c = 0;
 | 
			
		||||
                        for(i=0;i<l;i++) {
 | 
			
		||||
                            var d = parseInt(bufferBinValue[i]);
 | 
			
		||||
                            if (!isString && (isNaN(d) || d < 0 || d > 255)) {
 | 
			
		||||
                                valid = false;
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                            if (i>0) {
 | 
			
		||||
                                if (i%8 === 0) {
 | 
			
		||||
                                    if (i%16 === 0) {
 | 
			
		||||
                                        binBuffer.push("\n");
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        binBuffer.push("  ");
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    binBuffer.push(" ");
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            binBuffer.push((d<16?"0":"")+d.toString(16).toUpperCase());
 | 
			
		||||
                        }
 | 
			
		||||
                        if (valid) {
 | 
			
		||||
                            $("#node-input-buffer-type-string").toggle(isString);
 | 
			
		||||
                            $("#node-input-buffer-type-array").toggle(!isString);
 | 
			
		||||
                            bufferBinEditor.setValue(binBuffer.join(""),1);
 | 
			
		||||
                        }
 | 
			
		||||
                        return valid;
 | 
			
		||||
                    }
 | 
			
		||||
                    var bufferStringUpdate = function() {
 | 
			
		||||
                        var value = bufferStringEditor.getValue();
 | 
			
		||||
                        var isValidArray = false;
 | 
			
		||||
                        if (/^[\s]*\[[\s\S]*\][\s]*$/.test(value)) {
 | 
			
		||||
                            isValidArray = true;
 | 
			
		||||
                            try {
 | 
			
		||||
                                var data = JSON.parse(value);
 | 
			
		||||
                                isValidArray = buildBuffer(data);
 | 
			
		||||
                            } catch(err) {
 | 
			
		||||
                                isValidArray = false;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (!isValidArray) {
 | 
			
		||||
                            buildBuffer(value);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    bufferStringEditor.getSession().on('change', function() {
 | 
			
		||||
                        clearTimeout(changeTimer);
 | 
			
		||||
                        changeTimer = setTimeout(bufferStringUpdate,200);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    bufferStringUpdate();
 | 
			
		||||
 | 
			
		||||
                    dialogForm.i18n();
 | 
			
		||||
 | 
			
		||||
                    panels = RED.panels.create({
 | 
			
		||||
                        id:"node-input-buffer-panels",
 | 
			
		||||
                        resize: function(p1Height,p2Height) {
 | 
			
		||||
                            var p1 = $("#node-input-buffer-panel-str");
 | 
			
		||||
                            p1Height -= $(p1.children()[0]).outerHeight(true);
 | 
			
		||||
                            var editorRow = $(p1.children()[1]);
 | 
			
		||||
                            p1Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
 | 
			
		||||
                            $("#node-input-buffer-str").css("height",(p1Height-5)+"px");
 | 
			
		||||
                            bufferStringEditor.resize();
 | 
			
		||||
 | 
			
		||||
                            var p2 = $("#node-input-buffer-panel-bin");
 | 
			
		||||
                            editorRow = $(p2.children()[0]);
 | 
			
		||||
                            p2Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
 | 
			
		||||
                            $("#node-input-buffer-bin").css("height",(p2Height-5)+"px");
 | 
			
		||||
                            bufferBinEditor.resize();
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    $(".node-input-buffer-type").click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        RED.sidebar.info.set(RED._("bufferEditor.modeDesc"));
 | 
			
		||||
                        RED.sidebar.info.show();
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                },
 | 
			
		||||
                close: function() {
 | 
			
		||||
                    if (options.onclose) {
 | 
			
		||||
                        options.onclose();
 | 
			
		||||
                    }
 | 
			
		||||
                    bufferStringEditor.destroy();
 | 
			
		||||
                    bufferBinEditor.destroy();
 | 
			
		||||
                },
 | 
			
		||||
                show: function() {}
 | 
			
		||||
            }
 | 
			
		||||
            RED.tray.show(trayOptions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										325
									
								
								editor/js/ui/editors/expression.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								editor/js/ui/editors/expression.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,325 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.editor.types._expression = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var template = '<script type="text/x-red" data-template-name="_expression"><div id="node-input-expression-panels"><div id="node-input-expression-panel-expr" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><span class="node-input-expression-legacy"><i class="fa fa-exclamation-circle"></i> <span data-i18n="expressionEditor.compatMode"></span></span><button id="node-input-expression-reformat" class="editor-button editor-button-small"><span data-i18n="expressionEditor.format"></span></button></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="node-input-expression"></div></div></div><div id="node-input-expression-panel-info" class="red-ui-panel"><div class="form-row"><ul id="node-input-expression-tabs"></ul><div id="node-input-expression-tab-help" class="node-input-expression-tab-content hide"><div><select id="node-input-expression-func"></select><button id="node-input-expression-func-insert" class="editor-button" data-i18n="expressionEditor.insert"></button></div><div id="node-input-expression-help"></div></div><div id="node-input-expression-tab-test" class="node-input-expression-tab-content hide"><div><span style="display: inline-block; width: calc(50% - 5px);"><span data-i18n="expressionEditor.data"></span><button style="float: right; margin-right: 5px;" id="node-input-example-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button></span><span style="display: inline-block; width: calc(50% - 5px);" data-i18n="expressionEditor.result"></span></div><div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-data"></div><div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-result"></div></div></div></div></div></script>';
 | 
			
		||||
    var expressionTestCache = {};
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            $(template).appendTo(document.body);
 | 
			
		||||
        },
 | 
			
		||||
        show: function(options) {
 | 
			
		||||
            var expressionTestCacheId = options.parent||"_";
 | 
			
		||||
            var value = options.value;
 | 
			
		||||
            var onComplete = options.complete;
 | 
			
		||||
            var type = "_expression"
 | 
			
		||||
            RED.view.state(RED.state.EDITING);
 | 
			
		||||
            var expressionEditor;
 | 
			
		||||
            var testDataEditor;
 | 
			
		||||
            var testResultEditor
 | 
			
		||||
            var panels;
 | 
			
		||||
 | 
			
		||||
            var trayOptions = {
 | 
			
		||||
                title: options.title,
 | 
			
		||||
                width: "inherit",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-cancel",
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-ok",
 | 
			
		||||
                        text: RED._("common.label.done"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            $("#node-input-expression-help").text("");
 | 
			
		||||
                            onComplete(expressionEditor.getValue());
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                resize: function(dimensions) {
 | 
			
		||||
                    var height = $("#dialog-form").height();
 | 
			
		||||
                    if (panels) {
 | 
			
		||||
                        panels.resize(height);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                },
 | 
			
		||||
                open: function(tray) {
 | 
			
		||||
                    var trayBody = tray.find('.editor-tray-body');
 | 
			
		||||
                    trayBody.addClass("node-input-expression-editor")
 | 
			
		||||
                    var dialogForm = RED.editor.buildEditForm(tray.find('.editor-tray-body'),'dialog-form','_expression','editor');
 | 
			
		||||
                    var funcSelect = $("#node-input-expression-func");
 | 
			
		||||
                    Object.keys(jsonata.functions).forEach(function(f) {
 | 
			
		||||
                        funcSelect.append($("<option></option>").val(f).text(f));
 | 
			
		||||
                    })
 | 
			
		||||
                    funcSelect.change(function(e) {
 | 
			
		||||
                        var f = $(this).val();
 | 
			
		||||
                        var args = RED._('jsonata:'+f+".args",{defaultValue:''});
 | 
			
		||||
                        var title = "<h5>"+f+"("+args+")</h5>";
 | 
			
		||||
                        var body = marked(RED._('jsonata:'+f+'.desc',{defaultValue:''}));
 | 
			
		||||
                        $("#node-input-expression-help").html(title+"<p>"+body+"</p>");
 | 
			
		||||
 | 
			
		||||
                    })
 | 
			
		||||
                    expressionEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-expression',
 | 
			
		||||
                        value: "",
 | 
			
		||||
                        mode:"ace/mode/jsonata",
 | 
			
		||||
                        options: {
 | 
			
		||||
                            enableBasicAutocompletion:true,
 | 
			
		||||
                            enableSnippets:true,
 | 
			
		||||
                            enableLiveAutocompletion: true
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    var currentToken = null;
 | 
			
		||||
                    var currentTokenPos = -1;
 | 
			
		||||
                    var currentFunctionMarker = null;
 | 
			
		||||
 | 
			
		||||
                    expressionEditor.getSession().setValue(value||"",-1);
 | 
			
		||||
                    expressionEditor.on("changeSelection", function() {
 | 
			
		||||
                        var c = expressionEditor.getCursorPosition();
 | 
			
		||||
                        var token = expressionEditor.getSession().getTokenAt(c.row,c.column);
 | 
			
		||||
                        if (token !== currentToken || (token && /paren/.test(token.type) && c.column !== currentTokenPos)) {
 | 
			
		||||
                            currentToken = token;
 | 
			
		||||
                            var r,p;
 | 
			
		||||
                            var scopedFunction = null;
 | 
			
		||||
                            if (token && token.type === 'keyword') {
 | 
			
		||||
                                r = c.row;
 | 
			
		||||
                                scopedFunction = token;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                var depth = 0;
 | 
			
		||||
                                var next = false;
 | 
			
		||||
                                if (token) {
 | 
			
		||||
                                    if (token.type === 'paren.rparen') {
 | 
			
		||||
                                        // If this is a block of parens ')))', set
 | 
			
		||||
                                        // depth to offset against the cursor position
 | 
			
		||||
                                        // within the block
 | 
			
		||||
                                        currentTokenPos = c.column;
 | 
			
		||||
                                        depth = c.column - (token.start + token.value.length);
 | 
			
		||||
                                    }
 | 
			
		||||
                                    r = c.row;
 | 
			
		||||
                                    p = token.index;
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    r = c.row-1;
 | 
			
		||||
                                    p = -1;
 | 
			
		||||
                                }
 | 
			
		||||
                                while ( scopedFunction === null && r > -1) {
 | 
			
		||||
                                    var rowTokens = expressionEditor.getSession().getTokens(r);
 | 
			
		||||
                                    if (p === -1) {
 | 
			
		||||
                                        p = rowTokens.length-1;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    while (p > -1) {
 | 
			
		||||
                                        var type = rowTokens[p].type;
 | 
			
		||||
                                        if (next) {
 | 
			
		||||
                                            if (type === 'keyword') {
 | 
			
		||||
                                                scopedFunction = rowTokens[p];
 | 
			
		||||
                                                // console.log("HIT",scopedFunction);
 | 
			
		||||
                                                break;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            next = false;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        if (type === 'paren.lparen') {
 | 
			
		||||
                                            depth-=rowTokens[p].value.length;
 | 
			
		||||
                                        } else if (type === 'paren.rparen') {
 | 
			
		||||
                                            depth+=rowTokens[p].value.length;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        if (depth < 0) {
 | 
			
		||||
                                            next = true;
 | 
			
		||||
                                            depth = 0;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        // console.log(r,p,depth,next,rowTokens[p]);
 | 
			
		||||
                                        p--;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if (!scopedFunction) {
 | 
			
		||||
                                        r--;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            expressionEditor.session.removeMarker(currentFunctionMarker);
 | 
			
		||||
                            if (scopedFunction) {
 | 
			
		||||
                            //console.log(token,.map(function(t) { return t.type}));
 | 
			
		||||
                                funcSelect.val(scopedFunction.value).change();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    dialogForm.i18n();
 | 
			
		||||
                    $("#node-input-expression-func-insert").click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        var pos = expressionEditor.getCursorPosition();
 | 
			
		||||
                        var f = funcSelect.val();
 | 
			
		||||
                        var snippet = jsonata.getFunctionSnippet(f);
 | 
			
		||||
                        expressionEditor.insertSnippet(snippet);
 | 
			
		||||
                        expressionEditor.focus();
 | 
			
		||||
                    });
 | 
			
		||||
                    $("#node-input-expression-reformat").click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        var v = expressionEditor.getValue()||"";
 | 
			
		||||
                        try {
 | 
			
		||||
                            v = jsonata.format(v);
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            // TODO: do an optimistic auto-format
 | 
			
		||||
                        }
 | 
			
		||||
                        expressionEditor.getSession().setValue(v||"",-1);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    var tabs = RED.tabs.create({
 | 
			
		||||
                        element: $("#node-input-expression-tabs"),
 | 
			
		||||
                        onchange:function(tab) {
 | 
			
		||||
                            $(".node-input-expression-tab-content").hide();
 | 
			
		||||
                            tab.content.show();
 | 
			
		||||
                            trayOptions.resize();
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
                    tabs.addTab({
 | 
			
		||||
                        id: 'expression-help',
 | 
			
		||||
                        label: RED._('expressionEditor.functionReference'),
 | 
			
		||||
                        content: $("#node-input-expression-tab-help")
 | 
			
		||||
                    });
 | 
			
		||||
                    tabs.addTab({
 | 
			
		||||
                        id: 'expression-tests',
 | 
			
		||||
                        label: RED._('expressionEditor.test'),
 | 
			
		||||
                        content: $("#node-input-expression-tab-test")
 | 
			
		||||
                    });
 | 
			
		||||
                    testDataEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-expression-test-data',
 | 
			
		||||
                        value: expressionTestCache[expressionTestCacheId] || '{\n    "payload": "hello world"\n}',
 | 
			
		||||
                        mode:"ace/mode/json",
 | 
			
		||||
                        lineNumbers: false
 | 
			
		||||
                    });
 | 
			
		||||
                    var changeTimer;
 | 
			
		||||
                    $(".node-input-expression-legacy").click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        RED.sidebar.info.set(RED._("expressionEditor.compatModeDesc"));
 | 
			
		||||
                        RED.sidebar.info.show();
 | 
			
		||||
                    })
 | 
			
		||||
                    var testExpression = function() {
 | 
			
		||||
                        var value = testDataEditor.getValue();
 | 
			
		||||
                        var parsedData;
 | 
			
		||||
                        var currentExpression = expressionEditor.getValue();
 | 
			
		||||
                        var expr;
 | 
			
		||||
                        var usesContext = false;
 | 
			
		||||
                        var legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
 | 
			
		||||
                        $(".node-input-expression-legacy").toggle(legacyMode);
 | 
			
		||||
                        try {
 | 
			
		||||
                            expr = jsonata(currentExpression);
 | 
			
		||||
                            expr.assign('flowContext',function(val) {
 | 
			
		||||
                                usesContext = true;
 | 
			
		||||
                                return null;
 | 
			
		||||
                            });
 | 
			
		||||
                            expr.assign('globalContext',function(val) {
 | 
			
		||||
                                usesContext = true;
 | 
			
		||||
                                return null;
 | 
			
		||||
                            });
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1);
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        try {
 | 
			
		||||
                            parsedData = JSON.parse(value);
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            testResultEditor.setValue(RED._("expressionEditor.errors.invalid-msg",{message:err.toString()}))
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        try {
 | 
			
		||||
                            var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
 | 
			
		||||
                            if (usesContext) {
 | 
			
		||||
                                testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            var formattedResult;
 | 
			
		||||
                            if (result !== undefined) {
 | 
			
		||||
                                formattedResult = JSON.stringify(result,null,4);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                formattedResult = RED._("expressionEditor.noMatch");
 | 
			
		||||
                            }
 | 
			
		||||
                            testResultEditor.setValue(formattedResult,-1);
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    testDataEditor.getSession().on('change', function() {
 | 
			
		||||
                        clearTimeout(changeTimer);
 | 
			
		||||
                        changeTimer = setTimeout(testExpression,200);
 | 
			
		||||
                        expressionTestCache[expressionTestCacheId] = testDataEditor.getValue();
 | 
			
		||||
                    });
 | 
			
		||||
                    expressionEditor.getSession().on('change', function() {
 | 
			
		||||
                        clearTimeout(changeTimer);
 | 
			
		||||
                        changeTimer = setTimeout(testExpression,200);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    testResultEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-expression-test-result',
 | 
			
		||||
                        value: "",
 | 
			
		||||
                        mode:"ace/mode/json",
 | 
			
		||||
                        lineNumbers: false,
 | 
			
		||||
                        readOnly: true
 | 
			
		||||
                    });
 | 
			
		||||
                    panels = RED.panels.create({
 | 
			
		||||
                        id:"node-input-expression-panels",
 | 
			
		||||
                        resize: function(p1Height,p2Height) {
 | 
			
		||||
                            var p1 = $("#node-input-expression-panel-expr");
 | 
			
		||||
                            p1Height -= $(p1.children()[0]).outerHeight(true);
 | 
			
		||||
                            var editorRow = $(p1.children()[1]);
 | 
			
		||||
                            p1Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
 | 
			
		||||
                            $("#node-input-expression").css("height",(p1Height-5)+"px");
 | 
			
		||||
                            expressionEditor.resize();
 | 
			
		||||
 | 
			
		||||
                            var p2 = $("#node-input-expression-panel-info > .form-row > div:first-child");
 | 
			
		||||
                            p2Height -= p2.outerHeight(true) + 20;
 | 
			
		||||
                            $(".node-input-expression-tab-content").height(p2Height);
 | 
			
		||||
                            $("#node-input-expression-test-data").css("height",(p2Height-5)+"px");
 | 
			
		||||
                            testDataEditor.resize();
 | 
			
		||||
                            $("#node-input-expression-test-result").css("height",(p2Height-5)+"px");
 | 
			
		||||
                            testResultEditor.resize();
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    $("#node-input-example-reformat").click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        var v = testDataEditor.getValue()||"";
 | 
			
		||||
                        try {
 | 
			
		||||
                            v = JSON.stringify(JSON.parse(v),null,4);
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            // TODO: do an optimistic auto-format
 | 
			
		||||
                        }
 | 
			
		||||
                        testDataEditor.getSession().setValue(v||"",-1);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    testExpression();
 | 
			
		||||
                },
 | 
			
		||||
                close: function() {
 | 
			
		||||
                    if (options.onclose) {
 | 
			
		||||
                        options.onclose();
 | 
			
		||||
                    }
 | 
			
		||||
                    expressionEditor.destroy();
 | 
			
		||||
                    testDataEditor.destroy();
 | 
			
		||||
                },
 | 
			
		||||
                show: function() {}
 | 
			
		||||
            }
 | 
			
		||||
            RED.tray.show(trayOptions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										102
									
								
								editor/js/ui/editors/js.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								editor/js/ui/editors/js.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.editor.types._js = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var template = '<script type="text/x-red" data-template-name="_js"><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-js"></div></div></script>';
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            $(template).appendTo(document.body);
 | 
			
		||||
        },
 | 
			
		||||
        show: function(options) {
 | 
			
		||||
            var value = options.value;
 | 
			
		||||
            var onComplete = options.complete;
 | 
			
		||||
            var type = "_js"
 | 
			
		||||
            RED.view.state(RED.state.EDITING);
 | 
			
		||||
            var expressionEditor;
 | 
			
		||||
            var changeTimer;
 | 
			
		||||
 | 
			
		||||
            var trayOptions = {
 | 
			
		||||
                title: options.title,
 | 
			
		||||
                width: "inherit",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-cancel",
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-ok",
 | 
			
		||||
                        text: RED._("common.label.done"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                resize: function(dimensions) {
 | 
			
		||||
                    var rows = $("#dialog-form>div:not(.node-text-editor-row)");
 | 
			
		||||
                    var editorRow = $("#dialog-form>div.node-text-editor-row");
 | 
			
		||||
                    var height = $("#dialog-form").height();
 | 
			
		||||
                    for (var i=0;i<rows.size();i++) {
 | 
			
		||||
                        height -= $(rows[i]).outerHeight(true);
 | 
			
		||||
                    }
 | 
			
		||||
                    $(".node-text-editor").css("height",height+"px");
 | 
			
		||||
                    expressionEditor.resize();
 | 
			
		||||
                },
 | 
			
		||||
                open: function(tray) {
 | 
			
		||||
                    var trayBody = tray.find('.editor-tray-body');
 | 
			
		||||
                    var dialogForm = RED.editor.buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
 | 
			
		||||
                    expressionEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-js',
 | 
			
		||||
                        mode: 'ace/mode/javascript',
 | 
			
		||||
                        value: value,
 | 
			
		||||
                        globals: {
 | 
			
		||||
                            msg:true,
 | 
			
		||||
                            context:true,
 | 
			
		||||
                            RED: true,
 | 
			
		||||
                            util: true,
 | 
			
		||||
                            flow: true,
 | 
			
		||||
                            global: true,
 | 
			
		||||
                            console: true,
 | 
			
		||||
                            Buffer: true,
 | 
			
		||||
                            setTimeout: true,
 | 
			
		||||
                            clearTimeout: true,
 | 
			
		||||
                            setInterval: true,
 | 
			
		||||
                            clearInterval: true
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    if (options.cursor) {
 | 
			
		||||
                        expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
 | 
			
		||||
                    }
 | 
			
		||||
                    dialogForm.i18n();
 | 
			
		||||
                },
 | 
			
		||||
                close: function() {
 | 
			
		||||
                    expressionEditor.destroy();
 | 
			
		||||
                    if (options.onclose) {
 | 
			
		||||
                        options.onclose();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                show: function() {}
 | 
			
		||||
            }
 | 
			
		||||
            RED.tray.show(trayOptions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										118
									
								
								editor/js/ui/editors/json.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								editor/js/ui/editors/json.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.editor.types._json = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var template = '<script type="text/x-red" data-template-name="_json"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button id="node-input-json-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button></div><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div></div></script>';
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            $(template).appendTo(document.body);
 | 
			
		||||
        },
 | 
			
		||||
        show: function(options) {
 | 
			
		||||
            var value = options.value;
 | 
			
		||||
            var onComplete = options.complete;
 | 
			
		||||
            var type = "_json"
 | 
			
		||||
            RED.view.state(RED.state.EDITING);
 | 
			
		||||
            var expressionEditor;
 | 
			
		||||
            var changeTimer;
 | 
			
		||||
 | 
			
		||||
            var checkValid = function() {
 | 
			
		||||
                var v = expressionEditor.getValue();
 | 
			
		||||
                try {
 | 
			
		||||
                    JSON.parse(v);
 | 
			
		||||
                    $("#node-dialog-ok").removeClass('disabled');
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch(err) {
 | 
			
		||||
                    $("#node-dialog-ok").addClass('disabled');
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var trayOptions = {
 | 
			
		||||
                title: options.title,
 | 
			
		||||
                width: "inherit",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-cancel",
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-ok",
 | 
			
		||||
                        text: RED._("common.label.done"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            if (options.requireValid && !checkValid()) {
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                            onComplete(expressionEditor.getValue());
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                resize: function(dimensions) {
 | 
			
		||||
                    var rows = $("#dialog-form>div:not(.node-text-editor-row)");
 | 
			
		||||
                    var editorRow = $("#dialog-form>div.node-text-editor-row");
 | 
			
		||||
                    var height = $("#dialog-form").height();
 | 
			
		||||
                    for (var i=0;i<rows.size();i++) {
 | 
			
		||||
                        height -= $(rows[i]).outerHeight(true);
 | 
			
		||||
                    }
 | 
			
		||||
                    height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
 | 
			
		||||
                    $(".node-text-editor").css("height",height+"px");
 | 
			
		||||
                    expressionEditor.resize();
 | 
			
		||||
                },
 | 
			
		||||
                open: function(tray) {
 | 
			
		||||
                    var trayBody = tray.find('.editor-tray-body');
 | 
			
		||||
                    var dialogForm = RED.editor.buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
 | 
			
		||||
                    expressionEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-json',
 | 
			
		||||
                        value: "",
 | 
			
		||||
                        mode:"ace/mode/json"
 | 
			
		||||
                    });
 | 
			
		||||
                    expressionEditor.getSession().setValue(value||"",-1);
 | 
			
		||||
                    if (options.requireValid) {
 | 
			
		||||
                        expressionEditor.getSession().on('change', function() {
 | 
			
		||||
                            clearTimeout(changeTimer);
 | 
			
		||||
                            changeTimer = setTimeout(checkValid,200);
 | 
			
		||||
                        });
 | 
			
		||||
                        checkValid();
 | 
			
		||||
                    }
 | 
			
		||||
                    $("#node-input-json-reformat").click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        var v = expressionEditor.getValue()||"";
 | 
			
		||||
                        try {
 | 
			
		||||
                            v = JSON.stringify(JSON.parse(v),null,4);
 | 
			
		||||
                        } catch(err) {
 | 
			
		||||
                            // TODO: do an optimistic auto-format
 | 
			
		||||
                        }
 | 
			
		||||
                        expressionEditor.getSession().setValue(v||"",-1);
 | 
			
		||||
                    });
 | 
			
		||||
                    dialogForm.i18n();
 | 
			
		||||
                },
 | 
			
		||||
                close: function() {
 | 
			
		||||
                    expressionEditor.destroy();
 | 
			
		||||
                    if (options.onclose) {
 | 
			
		||||
                        options.onclose();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                show: function() {}
 | 
			
		||||
            }
 | 
			
		||||
            RED.tray.show(trayOptions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										90
									
								
								editor/js/ui/editors/markdown.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								editor/js/ui/editors/markdown.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.editor.types._markdown = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var template = '<script type="text/x-red" data-template-name="_markdown"><div class="form-row" id="node-input-markdown-title" style="margin-bottom: 3px; text-align: right;"></div><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-markdown"></div></div></script>';
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            $(template).appendTo(document.body);
 | 
			
		||||
        },
 | 
			
		||||
        show: function(options) {
 | 
			
		||||
            var value = options.value;
 | 
			
		||||
            var onComplete = options.complete;
 | 
			
		||||
            var type = "_markdown"
 | 
			
		||||
            RED.view.state(RED.state.EDITING);
 | 
			
		||||
            var expressionEditor;
 | 
			
		||||
 | 
			
		||||
            var trayOptions = {
 | 
			
		||||
                title: options.title,
 | 
			
		||||
                width: "inherit",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-cancel",
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-dialog-ok",
 | 
			
		||||
                        text: RED._("common.label.done"),
 | 
			
		||||
                        class: "primary",
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            onComplete(expressionEditor.getValue());
 | 
			
		||||
                            RED.tray.close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                resize: function(dimensions) {
 | 
			
		||||
                    var rows = $("#dialog-form>div:not(.node-text-editor-row)");
 | 
			
		||||
                    var editorRow = $("#dialog-form>div.node-text-editor-row");
 | 
			
		||||
                    var height = $("#dialog-form").height();
 | 
			
		||||
                    for (var i=0;i<rows.size();i++) {
 | 
			
		||||
                        height -= $(rows[i]).outerHeight(true);
 | 
			
		||||
                    }
 | 
			
		||||
                    height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
 | 
			
		||||
                    $(".node-text-editor").css("height",height+"px");
 | 
			
		||||
                    expressionEditor.resize();
 | 
			
		||||
                },
 | 
			
		||||
                open: function(tray) {
 | 
			
		||||
                    var trayBody = tray.find('.editor-tray-body');
 | 
			
		||||
                    var dialogForm = RED.editor.buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
 | 
			
		||||
                    expressionEditor = RED.editor.createEditor({
 | 
			
		||||
                        id: 'node-input-markdown',
 | 
			
		||||
                        value: value,
 | 
			
		||||
                        mode:"ace/mode/markdown"
 | 
			
		||||
                    });
 | 
			
		||||
                    if (options.header) {
 | 
			
		||||
                        options.header.appendTo(tray.find('#node-input-markdown-title'));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    dialogForm.i18n();
 | 
			
		||||
                },
 | 
			
		||||
                close: function() {
 | 
			
		||||
                    expressionEditor.destroy();
 | 
			
		||||
                    if (options.onclose) {
 | 
			
		||||
                        options.onclose();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                show: function() {}
 | 
			
		||||
            }
 | 
			
		||||
            RED.tray.show(trayOptions);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
@@ -57,8 +57,24 @@ RED.keyboard = (function() {
 | 
			
		||||
        173:189
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function migrateOldKeymap() {
 | 
			
		||||
        if ('localStorage' in window && window['localStorage'] !== null) {
 | 
			
		||||
            var oldKeyMap = localStorage.getItem("keymap");
 | 
			
		||||
            if (oldKeyMap !== null) {
 | 
			
		||||
                localStorage.removeItem("keymap");
 | 
			
		||||
                var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
                currentEditorSettings.keymap  = JSON.parse(oldKeyMap);
 | 
			
		||||
                RED.settings.set('editor',currentEditorSettings);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function init() {
 | 
			
		||||
        var userKeymap = RED.settings.get('keymap') || {};
 | 
			
		||||
        // Migrate from pre-0.18
 | 
			
		||||
        migrateOldKeymap();
 | 
			
		||||
 | 
			
		||||
        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
        var userKeymap = currentEditorSettings.keymap || {};
 | 
			
		||||
 | 
			
		||||
        $.getJSON("red/keymap.json",function(data) {
 | 
			
		||||
            for (var scope in data) {
 | 
			
		||||
                if (data.hasOwnProperty(scope)) {
 | 
			
		||||
@@ -67,12 +83,12 @@ RED.keyboard = (function() {
 | 
			
		||||
                        if (keys.hasOwnProperty(key)) {
 | 
			
		||||
                            if (!userKeymap.hasOwnProperty(keys[key])) {
 | 
			
		||||
                                addHandler(scope,key,keys[key],false);
 | 
			
		||||
                                defaultKeyMap[keys[key]] = {
 | 
			
		||||
                                    scope:scope,
 | 
			
		||||
                                    key:key,
 | 
			
		||||
                                    user:false
 | 
			
		||||
                                };
 | 
			
		||||
                            }
 | 
			
		||||
                            defaultKeyMap[keys[key]] = {
 | 
			
		||||
                                scope:scope,
 | 
			
		||||
                                key:key,
 | 
			
		||||
                                user:false
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -89,7 +105,7 @@ RED.keyboard = (function() {
 | 
			
		||||
 | 
			
		||||
        RED.userSettings.add({
 | 
			
		||||
            id:'keyboard',
 | 
			
		||||
            title: 'Keyboard',
 | 
			
		||||
            title: RED._("keyboard.keyboard"),
 | 
			
		||||
            get: getSettingsPane,
 | 
			
		||||
            focus: function() {
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
@@ -350,7 +366,8 @@ RED.keyboard = (function() {
 | 
			
		||||
                $(this).toggleClass("input-error",!valid);
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            var scopeSelect = $('<select><option value="*">global</option><option value="workspace">workspace</option></select>').appendTo(scope);
 | 
			
		||||
            var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
 | 
			
		||||
            scopeSelect.i18n();
 | 
			
		||||
            scopeSelect.val(object.scope||'*');
 | 
			
		||||
 | 
			
		||||
            var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope);
 | 
			
		||||
@@ -368,8 +385,12 @@ RED.keyboard = (function() {
 | 
			
		||||
                container.removeClass('keyboard-shortcut-entry-expanded');
 | 
			
		||||
                var shortcut = RED.keyboard.getShortcut(object.id);
 | 
			
		||||
                var userKeymap = RED.settings.get('keymap') || {};
 | 
			
		||||
                delete userKeymap[object.id];
 | 
			
		||||
                RED.settings.set('keymap',userKeymap);
 | 
			
		||||
 | 
			
		||||
                var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
                var userKeymap = currentEditorSettings.keymap || {};
 | 
			
		||||
                userKeymap[object.id] = null;
 | 
			
		||||
                currentEditorSettings.keymap = userKeymap;
 | 
			
		||||
                RED.settings.set('editor',currentEditorSettings);
 | 
			
		||||
 | 
			
		||||
                var obj = {
 | 
			
		||||
                    id:object.id,
 | 
			
		||||
@@ -418,9 +439,12 @@ RED.keyboard = (function() {
 | 
			
		||||
                            object.scope = scope;
 | 
			
		||||
                            RED.keyboard.add(object.scope,object.key,object.id,true);
 | 
			
		||||
                        }
 | 
			
		||||
                        var userKeymap = RED.settings.get('keymap') || {};
 | 
			
		||||
 | 
			
		||||
                        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
                        var userKeymap = currentEditorSettings.keymap || {};
 | 
			
		||||
                        userKeymap[object.id] = RED.keyboard.getShortcut(object.id);
 | 
			
		||||
                        RED.settings.set('keymap',userKeymap);
 | 
			
		||||
                        currentEditorSettings.keymap = userKeymap;
 | 
			
		||||
                        RED.settings.set('editor',currentEditorSettings);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -468,9 +492,9 @@ RED.keyboard = (function() {
 | 
			
		||||
        var pane = $('<div id="user-settings-tab-keyboard"></div>');
 | 
			
		||||
 | 
			
		||||
        $('<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input id="user-settings-tab-keyboard-filter" type="text" placeholder="filter actions"></div>'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-key">shortcut</div>'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-scope">scope</div>'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input id="user-settings-tab-keyboard-filter" type="text" data-i18n="[placeholder]keyboard.filterActions"></div>'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-key" data-i18n="keyboard.shortcut"></div>'+
 | 
			
		||||
        '<div class="keyboard-shortcut-entry-scope" data-i18n="keyboard.scope"></div>'+
 | 
			
		||||
        '</div>').appendTo(pane);
 | 
			
		||||
 | 
			
		||||
        pane.find("input").searchBox({
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,8 @@
 | 
			
		||||
 **/
 | 
			
		||||
RED.library = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var exportToLibraryDialog;
 | 
			
		||||
    var elementPrefix = "node-input-";
 | 
			
		||||
 | 
			
		||||
    function loadFlowLibrary() {
 | 
			
		||||
        $.getJSON("library/flows",function(data) {
 | 
			
		||||
@@ -38,7 +38,7 @@ RED.library = (function() {
 | 
			
		||||
                            li.className = "dropdown-submenu pull-left";
 | 
			
		||||
                            a = document.createElement("a");
 | 
			
		||||
                            a.href="#";
 | 
			
		||||
                            var label = i.replace(/^node-red-contrib-/,"").replace(/^node-red-node-/,"").replace(/-/," ").replace(/_/," ");
 | 
			
		||||
                            var label = i.replace(/^@.*\//,"").replace(/^node-red-contrib-/,"").replace(/^node-red-node-/,"").replace(/-/," ").replace(/_/," ");
 | 
			
		||||
                            a.innerHTML = label;
 | 
			
		||||
                            li.appendChild(a);
 | 
			
		||||
                            li.appendChild(buildMenu(data.d[i],root+(root!==""?"/":"")+i));
 | 
			
		||||
@@ -86,6 +86,7 @@ RED.library = (function() {
 | 
			
		||||
        var libraryData = {};
 | 
			
		||||
        var selectedLibraryItem = null;
 | 
			
		||||
        var libraryEditor = null;
 | 
			
		||||
        elementPrefix = options.elementPrefix || "node-input-";
 | 
			
		||||
 | 
			
		||||
        // Orion editor has set/getText
 | 
			
		||||
        // ACE editor has set/getValue
 | 
			
		||||
@@ -157,8 +158,8 @@ RED.library = (function() {
 | 
			
		||||
            return ul;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $('#node-input-name').css("width","66%").after(
 | 
			
		||||
            '<div class="btn-group" style="margin-left: 5px;">'+
 | 
			
		||||
        $('#'+elementPrefix+"name").css("width","calc(100% - 52px)").after(
 | 
			
		||||
            '<div class="btn-group" style="margin-left:5px;">'+
 | 
			
		||||
            '<a id="node-input-'+options.type+'-lookup" class="editor-button" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></a>'+
 | 
			
		||||
            '<ul class="dropdown-menu pull-right" role="menu">'+
 | 
			
		||||
            '<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">'+RED._("library.openLibrary")+'</a></li>'+
 | 
			
		||||
@@ -187,7 +188,7 @@ RED.library = (function() {
 | 
			
		||||
 | 
			
		||||
        $('#node-input-'+options.type+'-menu-save-library').click(function(e) {
 | 
			
		||||
            //var found = false;
 | 
			
		||||
            var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
 | 
			
		||||
            var name = $("#"+elementPrefix+"name").val().replace(/(^\s*)|(\s*$)/g,"");
 | 
			
		||||
 | 
			
		||||
            //var buildPathList = function(data,root) {
 | 
			
		||||
            //    var paths = [];
 | 
			
		||||
@@ -264,7 +265,7 @@ RED.library = (function() {
 | 
			
		||||
                        if (selectedLibraryItem) {
 | 
			
		||||
                            for (var i=0; i<options.fields.length; i++) {
 | 
			
		||||
                                var field = options.fields[i];
 | 
			
		||||
                                $("#node-input-"+field).val(selectedLibraryItem[field]);
 | 
			
		||||
                                $("#"+elementPrefix+field).val(selectedLibraryItem[field]);
 | 
			
		||||
                            }
 | 
			
		||||
                            options.editor.setValue(libraryEditor.getValue(),-1);
 | 
			
		||||
                        }
 | 
			
		||||
@@ -286,7 +287,7 @@ RED.library = (function() {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        function saveToLibrary(overwrite) {
 | 
			
		||||
            var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
 | 
			
		||||
            var name = $("#"+elementPrefix+"name").val().replace(/(^\s*)|(\s*$)/g,"");
 | 
			
		||||
            if (name === "") {
 | 
			
		||||
                name = RED._("library.unnamedType",{type:options.type});
 | 
			
		||||
            }
 | 
			
		||||
@@ -330,7 +331,7 @@ RED.library = (function() {
 | 
			
		||||
                if (field == "name") {
 | 
			
		||||
                    data.name = name;
 | 
			
		||||
                } else {
 | 
			
		||||
                    data[field] = $("#node-input-"+field).val();
 | 
			
		||||
                    data[field] = $("#"+elementPrefix+field).val();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -413,12 +414,8 @@ RED.library = (function() {
 | 
			
		||||
 | 
			
		||||
            RED.events.on("view:selection-changed",function(selection) {
 | 
			
		||||
                if (!selection.nodes) {
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export",true);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-clipboard",true);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-library",true);
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export",false);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-clipboard",false);
 | 
			
		||||
                    RED.menu.setDisabled("menu-item-export-library",false);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,49 @@
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 **/
 | 
			
		||||
RED.notify = (function() {
 | 
			
		||||
RED.notifications = (function() {
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    // Example usage for a modal dialog with buttons
 | 
			
		||||
    var myNotification = RED.notify("This is the message to display",{
 | 
			
		||||
        modal: true,
 | 
			
		||||
        fixed: true,
 | 
			
		||||
        type: 'warning',
 | 
			
		||||
        buttons: [
 | 
			
		||||
            {
 | 
			
		||||
                text: "cancel",
 | 
			
		||||
                click: function(e) {
 | 
			
		||||
                    myNotification.close();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                text: "okay",
 | 
			
		||||
                class:"primary",
 | 
			
		||||
                click: function(e) {
 | 
			
		||||
                    myNotification.close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    });
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    var persistentNotifications = {};
 | 
			
		||||
 | 
			
		||||
    var currentNotifications = [];
 | 
			
		||||
    var c = 0;
 | 
			
		||||
    return function(msg,type,fixed,timeout) {
 | 
			
		||||
    function notify(msg,type,fixed,timeout) {
 | 
			
		||||
        var options = {};
 | 
			
		||||
        if (type !== null && typeof type === 'object') {
 | 
			
		||||
            options = type;
 | 
			
		||||
            fixed = options.fixed;
 | 
			
		||||
            timeout = options.timeout;
 | 
			
		||||
            type = options.type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (options.modal) {
 | 
			
		||||
            $("#full-shade").show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (currentNotifications.length > 4) {
 | 
			
		||||
            var ll = currentNotifications.length;
 | 
			
		||||
            for (var i = 0;ll > 4 && i<currentNotifications.length;i+=1) {
 | 
			
		||||
@@ -35,38 +74,123 @@ RED.notify = (function() {
 | 
			
		||||
        if (type) {
 | 
			
		||||
            n.className = "notification notification-"+type;
 | 
			
		||||
        }
 | 
			
		||||
        if (options.width) {
 | 
			
		||||
            var parentWidth = $("#notifications").width();
 | 
			
		||||
            if (options.width > parentWidth) {
 | 
			
		||||
                var margin = -(options.width-parentWidth)/2;
 | 
			
		||||
                $(n).css({
 | 
			
		||||
                    width: options.width+"px",
 | 
			
		||||
                    marginLeft: margin+"px"
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        n.style.display = "none";
 | 
			
		||||
        if (typeof msg === "string") {
 | 
			
		||||
            if (!/<p>/i.test(msg)) {
 | 
			
		||||
                msg = "<p>"+msg+"</p>";
 | 
			
		||||
            }
 | 
			
		||||
            n.innerHTML = msg;
 | 
			
		||||
        } else {
 | 
			
		||||
            $(n).append(msg);
 | 
			
		||||
        }
 | 
			
		||||
        if (options.buttons) {
 | 
			
		||||
            var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(n)
 | 
			
		||||
            options.buttons.forEach(function(buttonDef) {
 | 
			
		||||
                var b = $('<button>').html(buttonDef.text).click(buttonDef.click).appendTo(buttonSet);
 | 
			
		||||
                if (buttonDef.id) {
 | 
			
		||||
                    b.attr('id',buttonDef.id);
 | 
			
		||||
                }
 | 
			
		||||
                if (buttonDef.class) {
 | 
			
		||||
                    b.addClass(buttonDef.class);
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $("#notifications").append(n);
 | 
			
		||||
        $(n).slideDown(300);
 | 
			
		||||
        n.close = (function() {
 | 
			
		||||
            var nn = n;
 | 
			
		||||
            return function() {
 | 
			
		||||
                if (nn.closed) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                nn.closed = true;
 | 
			
		||||
                currentNotifications.splice(currentNotifications.indexOf(nn),1);
 | 
			
		||||
                if (options.id) {
 | 
			
		||||
                    delete persistentNotifications[options.id];
 | 
			
		||||
                    if (Object.keys(persistentNotifications).length === 0) {
 | 
			
		||||
                        notificationButtonWrapper.hide();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                $(nn).slideUp(300, function() {
 | 
			
		||||
                    nn.parentNode.removeChild(nn);
 | 
			
		||||
                });
 | 
			
		||||
                if (options.modal) {
 | 
			
		||||
                    $("#full-shade").hide();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        })();
 | 
			
		||||
        n.hideNotification = (function() {
 | 
			
		||||
            var nn = n;
 | 
			
		||||
            return function() {
 | 
			
		||||
                if (nn.closed) {
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                nn.hidden = true;
 | 
			
		||||
                $(nn).slideUp(300);
 | 
			
		||||
            }
 | 
			
		||||
        })();
 | 
			
		||||
        n.showNotification = (function() {
 | 
			
		||||
            var nn = n;
 | 
			
		||||
            return function() {
 | 
			
		||||
                if (nn.closed || !nn.hidden) {
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                nn.hidden = false;
 | 
			
		||||
                $(nn).slideDown(300);
 | 
			
		||||
            }
 | 
			
		||||
        })();
 | 
			
		||||
 | 
			
		||||
        n.update = (function() {
 | 
			
		||||
            var nn = n;
 | 
			
		||||
            return function(msg,timeout) {
 | 
			
		||||
            return function(msg,options) {
 | 
			
		||||
                if (typeof msg === "string") {
 | 
			
		||||
                    if (!/<p>/i.test(msg)) {
 | 
			
		||||
                        msg = "<p>"+msg+"</p>";
 | 
			
		||||
                    }
 | 
			
		||||
                    nn.innerHTML = msg;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $(nn).empty().append(msg);
 | 
			
		||||
                }
 | 
			
		||||
                var timeout;
 | 
			
		||||
                if (typeof options === 'number') {
 | 
			
		||||
                    timeout = options;
 | 
			
		||||
                } else if (options !== undefined) {
 | 
			
		||||
                    timeout = options.timeout;
 | 
			
		||||
                    if (options.buttons) {
 | 
			
		||||
                        var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn)
 | 
			
		||||
                        options.buttons.forEach(function(buttonDef) {
 | 
			
		||||
                            var b = $('<button>').text(buttonDef.text).click(buttonDef.click).appendTo(buttonSet);
 | 
			
		||||
                            if (buttonDef.id) {
 | 
			
		||||
                                b.attr('id',buttonDef.id);
 | 
			
		||||
                            }
 | 
			
		||||
                            if (buttonDef.class) {
 | 
			
		||||
                                b.addClass(buttonDef.class);
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (timeout !== undefined && timeout > 0) {
 | 
			
		||||
                    window.clearTimeout(nn.timeoutid);
 | 
			
		||||
                    nn.timeoutid = window.setTimeout(nn.close,timeout);
 | 
			
		||||
                } else {
 | 
			
		||||
                    window.clearTimeout(nn.timeoutid);
 | 
			
		||||
                }
 | 
			
		||||
                if (nn.hidden) {
 | 
			
		||||
                    nn.showNotification();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        })();
 | 
			
		||||
 | 
			
		||||
@@ -78,10 +202,48 @@ RED.notify = (function() {
 | 
			
		||||
                    window.clearTimeout(nn.timeoutid);
 | 
			
		||||
                };
 | 
			
		||||
            })());
 | 
			
		||||
            n.timeoutid = window.setTimeout(n.close,timeout||3000);
 | 
			
		||||
            n.timeoutid = window.setTimeout(n.close,timeout||5000);
 | 
			
		||||
        }
 | 
			
		||||
        currentNotifications.push(n);
 | 
			
		||||
        if (options.id) {
 | 
			
		||||
            persistentNotifications[options.id] = n;
 | 
			
		||||
            notificationButtonWrapper.show();
 | 
			
		||||
        }
 | 
			
		||||
        c+=1;
 | 
			
		||||
        return n;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RED.notify = notify;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function hidePersistent() {
 | 
			
		||||
        for(var i in persistentNotifications) {
 | 
			
		||||
            if (persistentNotifications.hasOwnProperty(i)) {
 | 
			
		||||
                persistentNotifications[i].hideNotification();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function showPersistent() {
 | 
			
		||||
        for(var i in persistentNotifications) {
 | 
			
		||||
            if (persistentNotifications.hasOwnProperty(i)) {
 | 
			
		||||
                persistentNotifications[i].showNotification();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var notificationButtonWrapper;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            notificationButtonWrapper = $('<li>'+
 | 
			
		||||
                '<a id="btn-notifications" class="button" href="#">'+
 | 
			
		||||
                '<i class="fa fa-warning"></i>'+
 | 
			
		||||
                '</a>'+
 | 
			
		||||
                '</li>').prependTo(".header-toolbar").hide();
 | 
			
		||||
            $('#btn-notifications').click(function() {
 | 
			
		||||
                showPersistent();
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
        notify: notify
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -75,27 +75,21 @@ RED.palette.editor = (function() {
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    function installNodeModule(id,version,shade,callback) {
 | 
			
		||||
    function installNodeModule(id,version,callback) {
 | 
			
		||||
        var requestBody = {
 | 
			
		||||
            module: id
 | 
			
		||||
        };
 | 
			
		||||
        if (callback === undefined) {
 | 
			
		||||
            callback = shade;
 | 
			
		||||
            shade = version;
 | 
			
		||||
        } else {
 | 
			
		||||
        if (version) {
 | 
			
		||||
            requestBody.version = version;
 | 
			
		||||
        }
 | 
			
		||||
        shade.show();
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            url:"nodes",
 | 
			
		||||
            type: "POST",
 | 
			
		||||
            data: JSON.stringify(requestBody),
 | 
			
		||||
            contentType: "application/json; charset=utf-8"
 | 
			
		||||
        }).done(function(data,textStatus,xhr) {
 | 
			
		||||
            shade.hide();
 | 
			
		||||
            callback();
 | 
			
		||||
        }).fail(function(xhr,textStatus,err) {
 | 
			
		||||
            shade.hide();
 | 
			
		||||
            callback(xhr);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -214,6 +208,8 @@ RED.palette.editor = (function() {
 | 
			
		||||
            if (nodeEntry) {
 | 
			
		||||
                var activeTypeCount = 0;
 | 
			
		||||
                var typeCount = 0;
 | 
			
		||||
                var errorCount = 0;
 | 
			
		||||
                nodeEntry.errorList.empty();
 | 
			
		||||
                nodeEntries[module].totalUseCount = 0;
 | 
			
		||||
                nodeEntries[module].setUseCount = {};
 | 
			
		||||
 | 
			
		||||
@@ -222,7 +218,10 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        var inUseCount = 0;
 | 
			
		||||
                        var set = moduleInfo.sets[setName];
 | 
			
		||||
                        var setElements = nodeEntry.sets[setName];
 | 
			
		||||
 | 
			
		||||
                        if (set.err) {
 | 
			
		||||
                            errorCount++;
 | 
			
		||||
                            $("<li>").text(set.err).appendTo(nodeEntry.errorList);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (set.enabled) {
 | 
			
		||||
                            activeTypeCount += set.types.length;
 | 
			
		||||
                        }
 | 
			
		||||
@@ -234,7 +233,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
                            if (set.enabled) {
 | 
			
		||||
                                var def = RED.nodes.getType(t);
 | 
			
		||||
                                if (def && def.color) {
 | 
			
		||||
                                    swatch.css({background:def.color});
 | 
			
		||||
                                    swatch.css({background:RED.utils.getNodeColor(t,def)});
 | 
			
		||||
                                    swatch.css({border: "1px solid "+getContrastingBorder(swatch.css('backgroundColor'))})
 | 
			
		||||
 | 
			
		||||
                                } else {
 | 
			
		||||
@@ -248,24 +247,31 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        nodeEntries[module].totalUseCount += inUseCount;
 | 
			
		||||
 | 
			
		||||
                        if (inUseCount > 0) {
 | 
			
		||||
                            setElements.enableButton.html(RED._('palette.editor.inuse'));
 | 
			
		||||
                            setElements.enableButton.text(RED._('palette.editor.inuse'));
 | 
			
		||||
                            setElements.enableButton.addClass('disabled');
 | 
			
		||||
                        } else {
 | 
			
		||||
                            setElements.enableButton.removeClass('disabled');
 | 
			
		||||
                            if (set.enabled) {
 | 
			
		||||
                                setElements.enableButton.html(RED._('palette.editor.disable'));
 | 
			
		||||
                                setElements.enableButton.text(RED._('palette.editor.disable'));
 | 
			
		||||
                            } else {
 | 
			
		||||
                                setElements.enableButton.html(RED._('palette.editor.enable'));
 | 
			
		||||
                                setElements.enableButton.text(RED._('palette.editor.enable'));
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        setElements.setRow.toggleClass("palette-module-set-disabled",!set.enabled);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (errorCount === 0) {
 | 
			
		||||
                    nodeEntry.errorRow.hide()
 | 
			
		||||
                } else {
 | 
			
		||||
                    nodeEntry.errorRow.show();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
 | 
			
		||||
                nodeEntry.setCount.html(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
 | 
			
		||||
                nodeEntry.setCount.text(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
 | 
			
		||||
 | 
			
		||||
                if (nodeEntries[module].totalUseCount > 0) {
 | 
			
		||||
                    nodeEntry.enableButton.html(RED._('palette.editor.inuse'));
 | 
			
		||||
                    nodeEntry.enableButton.text(RED._('palette.editor.inuse'));
 | 
			
		||||
                    nodeEntry.enableButton.addClass('disabled');
 | 
			
		||||
                    nodeEntry.removeButton.hide();
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -274,20 +280,20 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        nodeEntry.removeButton.css('display', 'inline-block');
 | 
			
		||||
                    }
 | 
			
		||||
                    if (activeTypeCount === 0) {
 | 
			
		||||
                        nodeEntry.enableButton.html(RED._('palette.editor.enableall'));
 | 
			
		||||
                        nodeEntry.enableButton.text(RED._('palette.editor.enableall'));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        nodeEntry.enableButton.html(RED._('palette.editor.disableall'));
 | 
			
		||||
                        nodeEntry.enableButton.text(RED._('palette.editor.disableall'));
 | 
			
		||||
                    }
 | 
			
		||||
                    nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (moduleInfo.pending_version) {
 | 
			
		||||
                nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
 | 
			
		||||
                nodeEntry.updateButton.html(RED._('palette.editor.updated')).addClass('disabled').show();
 | 
			
		||||
                nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').show();
 | 
			
		||||
            } else if (loadedIndex.hasOwnProperty(module)) {
 | 
			
		||||
                if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
 | 
			
		||||
                    nodeEntry.updateButton.show();
 | 
			
		||||
                    nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
 | 
			
		||||
                    nodeEntry.updateButton.text(RED._('palette.editor.update',{version:loadedIndex[module].version}));
 | 
			
		||||
                } else {
 | 
			
		||||
                    nodeEntry.updateButton.hide();
 | 
			
		||||
                }
 | 
			
		||||
@@ -361,7 +367,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
            loadedIndex = {};
 | 
			
		||||
            packageList.editableList('empty');
 | 
			
		||||
 | 
			
		||||
            $(".palette-module-shade-status").html(RED._('palette.editor.loading'));
 | 
			
		||||
            $(".palette-module-shade-status").text(RED._('palette.editor.loading'));
 | 
			
		||||
            var catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json'];
 | 
			
		||||
            catalogueLoadStatus = [];
 | 
			
		||||
            catalogueLoadErrors = false;
 | 
			
		||||
@@ -423,7 +429,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
 | 
			
		||||
        RED.userSettings.add({
 | 
			
		||||
            id:'palette',
 | 
			
		||||
            title: 'Palette',
 | 
			
		||||
            title: RED._("palette.editor.palette"),
 | 
			
		||||
            get: getSettingsPane,
 | 
			
		||||
            close: function() {
 | 
			
		||||
                settingsPane.detach();
 | 
			
		||||
@@ -467,7 +473,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
                if (filteredList[i].info.id === ns.module) {
 | 
			
		||||
                    var installButton = filteredList[i].elements.installButton;
 | 
			
		||||
                    installButton.addClass('disabled');
 | 
			
		||||
                    installButton.html(RED._('palette.editor.installed'));
 | 
			
		||||
                    installButton.text(RED._('palette.editor.installed'));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -483,7 +489,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        if (filteredList[i].info.id === ns.module) {
 | 
			
		||||
                            var installButton = filteredList[i].elements.installButton;
 | 
			
		||||
                            installButton.removeClass('disabled');
 | 
			
		||||
                            installButton.html(RED._('palette.editor.install'));
 | 
			
		||||
                            installButton.text(RED._('palette.editor.install'));
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@@ -589,61 +595,38 @@ RED.palette.editor = (function() {
 | 
			
		||||
                if (entry) {
 | 
			
		||||
                    var headerRow = $('<div>',{class:"palette-module-header"}).appendTo(container);
 | 
			
		||||
                    var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
 | 
			
		||||
                    $('<span>').html(entry.name).appendTo(titleRow);
 | 
			
		||||
                    $('<span>').text(entry.name).appendTo(titleRow);
 | 
			
		||||
                    var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
 | 
			
		||||
                    var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
 | 
			
		||||
                    var versionSpan = $('<span>').text(entry.version).appendTo(metaRow);
 | 
			
		||||
 | 
			
		||||
                    var errorRow = $('<div class="palette-module-meta palette-module-errors"><i class="fa fa-warning"></i></div>').hide().appendTo(headerRow);
 | 
			
		||||
                    var errorList = $('<ul class="palette-module-error-list"></ul>').appendTo(errorRow);
 | 
			
		||||
                    var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
 | 
			
		||||
                    var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
 | 
			
		||||
                    var setCount = $('<span>').appendTo(setButton);
 | 
			
		||||
                    var buttonGroup = $('<div>',{class:"palette-module-button-group"}).appendTo(buttonRow);
 | 
			
		||||
 | 
			
		||||
                    var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup);
 | 
			
		||||
                    var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').text(RED._('palette.editor.update')).appendTo(buttonGroup);
 | 
			
		||||
                    updateButton.attr('id','up_'+Math.floor(Math.random()*1000000000));
 | 
			
		||||
                    updateButton.click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        if ($(this).hasClass('disabled')) {
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        $("#palette-module-install-confirm").data('module',entry.name);
 | 
			
		||||
                        $("#palette-module-install-confirm").data('version',loadedIndex[entry.name].version);
 | 
			
		||||
                        $("#palette-module-install-confirm").data('shade',shade);
 | 
			
		||||
 | 
			
		||||
                        $("#palette-module-install-confirm-body").html(entry.local?
 | 
			
		||||
                            RED._("palette.editor.confirm.update.body"):
 | 
			
		||||
                            RED._("palette.editor.confirm.cannotUpdate.body")
 | 
			
		||||
                        );
 | 
			
		||||
                        $(".palette-module-install-confirm-button-install").hide();
 | 
			
		||||
                        $(".palette-module-install-confirm-button-remove").hide();
 | 
			
		||||
                        if (entry.local) {
 | 
			
		||||
                            $(".palette-module-install-confirm-button-update").show();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $(".palette-module-install-confirm-button-update").hide();
 | 
			
		||||
                        }
 | 
			
		||||
                        $("#palette-module-install-confirm")
 | 
			
		||||
                            .dialog('option', 'title',RED._("palette.editor.confirm.update.title"))
 | 
			
		||||
                            .dialog('open');
 | 
			
		||||
                        update(entry,loadedIndex[entry.name].version,container,function(err){});
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    var removeButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.remove')).appendTo(buttonGroup);
 | 
			
		||||
                    var removeButton = $('<a href="#" class="editor-button editor-button-small"></a>').text(RED._('palette.editor.remove')).appendTo(buttonGroup);
 | 
			
		||||
                    removeButton.attr('id','up_'+Math.floor(Math.random()*1000000000));
 | 
			
		||||
                    removeButton.click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
 | 
			
		||||
                        $("#palette-module-install-confirm").data('module',entry.name);
 | 
			
		||||
                        $("#palette-module-install-confirm").data('shade',shade);
 | 
			
		||||
                        $("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body"));
 | 
			
		||||
                        $(".palette-module-install-confirm-button-install").hide();
 | 
			
		||||
                        $(".palette-module-install-confirm-button-remove").show();
 | 
			
		||||
                        $(".palette-module-install-confirm-button-update").hide();
 | 
			
		||||
                        $("#palette-module-install-confirm")
 | 
			
		||||
                            .dialog('option', 'title', RED._("palette.editor.confirm.remove.title"))
 | 
			
		||||
                            .dialog('open');
 | 
			
		||||
                        remove(entry,container,function(err){});
 | 
			
		||||
                    })
 | 
			
		||||
                    if (!entry.local) {
 | 
			
		||||
                        removeButton.hide();
 | 
			
		||||
                    }
 | 
			
		||||
                    var enableButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.disableall')).appendTo(buttonGroup);
 | 
			
		||||
                    var enableButton = $('<a href="#" class="editor-button editor-button-small"></a>').text(RED._('palette.editor.disableall')).appendTo(buttonGroup);
 | 
			
		||||
 | 
			
		||||
                    var contentRow = $('<div>',{class:"palette-module-content"}).appendTo(container);
 | 
			
		||||
                    var shade = $('<div class="palette-module-shade hide"><img src="red/images/spin.svg" class="palette-spinner"/></div>').appendTo(container);
 | 
			
		||||
@@ -652,6 +635,8 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        updateButton: updateButton,
 | 
			
		||||
                        removeButton: removeButton,
 | 
			
		||||
                        enableButton: enableButton,
 | 
			
		||||
                        errorRow: errorRow,
 | 
			
		||||
                        errorList: errorList,
 | 
			
		||||
                        setCount: setCount,
 | 
			
		||||
                        container: container,
 | 
			
		||||
                        shade: shade,
 | 
			
		||||
@@ -681,9 +666,8 @@ RED.palette.editor = (function() {
 | 
			
		||||
                        set.types.forEach(function(t) {
 | 
			
		||||
                            var typeDiv = $('<div>',{class:"palette-module-type"}).appendTo(setRow);
 | 
			
		||||
                            typeSwatches[t] = $('<span>',{class:"palette-module-type-swatch"}).appendTo(typeDiv);
 | 
			
		||||
                            $('<span>',{class:"palette-module-type-node"}).html(t).appendTo(typeDiv);
 | 
			
		||||
                            $('<span>',{class:"palette-module-type-node"}).text(t).appendTo(typeDiv);
 | 
			
		||||
                        })
 | 
			
		||||
 | 
			
		||||
                        var enableButton = $('<a href="#" class="editor-button editor-button-small"></a>').appendTo(buttonGroup);
 | 
			
		||||
                        enableButton.click(function(evt) {
 | 
			
		||||
                            evt.preventDefault();
 | 
			
		||||
@@ -721,7 +705,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
                    })
 | 
			
		||||
                    refreshNodeModule(entry.name);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).html(RED._('search.empty')).appendTo(container);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
@@ -761,7 +745,7 @@ RED.palette.editor = (function() {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $('<span>').html(RED._("palette.editor.sort")+' ').appendTo(toolBar);
 | 
			
		||||
        $('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
 | 
			
		||||
        var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
 | 
			
		||||
        var sortAZ = $('<a href="#" class="sidebar-header-button-toggle selected" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
 | 
			
		||||
        var sortRecent = $('<a href="#" class="sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
 | 
			
		||||
@@ -803,13 +787,13 @@ RED.palette.editor = (function() {
 | 
			
		||||
            scrollOnAdd: false,
 | 
			
		||||
            addItem: function(container,i,object) {
 | 
			
		||||
                if (object.count) {
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).html(RED._('palette.editor.moduleCount',{count:object.count})).appendTo(container);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).text(RED._('palette.editor.moduleCount',{count:object.count})).appendTo(container);
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                if (object.more) {
 | 
			
		||||
                    container.addClass('palette-module-more');
 | 
			
		||||
                    var moreRow = $('<div>',{class:"palette-module-header palette-module"}).appendTo(container);
 | 
			
		||||
                    var moreLink = $('<a href="#"></a>').html(RED._('palette.editor.more',{count:object.more})).appendTo(moreRow);
 | 
			
		||||
                    var moreLink = $('<a href="#"></a>').text(RED._('palette.editor.more',{count:object.more})).appendTo(moreRow);
 | 
			
		||||
                    moreLink.click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        packageList.editableList('removeItem',object);
 | 
			
		||||
@@ -826,131 +810,158 @@ RED.palette.editor = (function() {
 | 
			
		||||
                    var entry = object.info;
 | 
			
		||||
                    var headerRow = $('<div>',{class:"palette-module-header"}).appendTo(container);
 | 
			
		||||
                    var titleRow = $('<div class="palette-module-meta"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
 | 
			
		||||
                    $('<span>',{class:"palette-module-name"}).html(entry.name||entry.id).appendTo(titleRow);
 | 
			
		||||
                    $('<span>',{class:"palette-module-name"}).text(entry.name||entry.id).appendTo(titleRow);
 | 
			
		||||
                    $('<a target="_blank" class="palette-module-link"><i class="fa fa-external-link"></i></a>').attr('href',entry.url).appendTo(titleRow);
 | 
			
		||||
                    var descRow = $('<div class="palette-module-meta"></div>').appendTo(headerRow);
 | 
			
		||||
                    $('<div>',{class:"palette-module-description"}).html(entry.description).appendTo(descRow);
 | 
			
		||||
                    $('<div>',{class:"palette-module-description"}).text(entry.description).appendTo(descRow);
 | 
			
		||||
 | 
			
		||||
                    var metaRow = $('<div class="palette-module-meta"></div>').appendTo(headerRow);
 | 
			
		||||
                    $('<span class="palette-module-version"><i class="fa fa-tag"></i> '+entry.version+'</span>').appendTo(metaRow);
 | 
			
		||||
                    $('<span class="palette-module-updated"><i class="fa fa-calendar"></i> '+formatUpdatedAt(entry.updated_at)+'</span>').appendTo(metaRow);
 | 
			
		||||
                    var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
 | 
			
		||||
                    var buttonGroup = $('<div>',{class:"palette-module-button-group"}).appendTo(buttonRow);
 | 
			
		||||
                    var shade = $('<div class="palette-module-shade hide"><img src="red/images/spin.svg" class="palette-spinner"/></div>').appendTo(container);
 | 
			
		||||
                    var installButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.install')).appendTo(buttonGroup);
 | 
			
		||||
                    var installButton = $('<a href="#" class="editor-button editor-button-small"></a>').text(RED._('palette.editor.install')).appendTo(buttonGroup);
 | 
			
		||||
                    installButton.click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        if (!$(this).hasClass('disabled')) {
 | 
			
		||||
                            $("#palette-module-install-confirm").data('module',entry.id);
 | 
			
		||||
                            $("#palette-module-install-confirm").data('version',entry.version);
 | 
			
		||||
                            $("#palette-module-install-confirm").data('url',entry.url);
 | 
			
		||||
                            $("#palette-module-install-confirm").data('shade',shade);
 | 
			
		||||
                            $("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body"));
 | 
			
		||||
                            $(".palette-module-install-confirm-button-install").show();
 | 
			
		||||
                            $(".palette-module-install-confirm-button-remove").hide();
 | 
			
		||||
                            $(".palette-module-install-confirm-button-update").hide();
 | 
			
		||||
                            $("#palette-module-install-confirm")
 | 
			
		||||
                                .dialog('option', 'title', RED._("palette.editor.confirm.install.title"))
 | 
			
		||||
                                .dialog('open');
 | 
			
		||||
                            install(entry,container,function(xhr) {});
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                    if (nodeEntries.hasOwnProperty(entry.id)) {
 | 
			
		||||
                        installButton.addClass('disabled');
 | 
			
		||||
                        installButton.html(RED._('palette.editor.installed'));
 | 
			
		||||
                        installButton.text(RED._('palette.editor.installed'));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    object.elements = {
 | 
			
		||||
                        installButton:installButton
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).html(RED._('search.empty')).appendTo(container);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $('<div id="palette-module-install-shade" class="palette-module-shade hide"><div class="palette-module-shade-status"></div><img src="red/images/spin.svg" class="palette-spinner"/></div>').appendTo(installTab);
 | 
			
		||||
 | 
			
		||||
        $('<div id="palette-module-install-confirm" class="hide"><form class="form-horizontal"><div id="palette-module-install-confirm-body" class="node-dialog-confirm-row"></div></form></div>').appendTo(document.body);
 | 
			
		||||
        $("#palette-module-install-confirm").dialog({
 | 
			
		||||
            title: RED._('palette.editor.confirm.title'),
 | 
			
		||||
    }
 | 
			
		||||
    function update(entry,version,container,done) {
 | 
			
		||||
        if (RED.settings.theme('palette.editable') === false) {
 | 
			
		||||
            done(new Error('Palette not editable'));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        var notification = RED.notify(RED._("palette.editor.confirm.update.body",{module:entry.name}),{
 | 
			
		||||
            modal: true,
 | 
			
		||||
            autoOpen: false,
 | 
			
		||||
            width: 550,
 | 
			
		||||
            height: "auto",
 | 
			
		||||
            fixed: true,
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("common.label.cancel"),
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        $( this ).dialog( "close" );
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("palette.editor.confirm.button.review"),
 | 
			
		||||
                    class: "primary palette-module-install-confirm-button-install",
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        var url = $(this).data('url');
 | 
			
		||||
                        window.open(url);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("palette.editor.confirm.button.install"),
 | 
			
		||||
                    class: "primary palette-module-install-confirm-button-install",
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        var id = $(this).data('module');
 | 
			
		||||
                        var version = $(this).data('version');
 | 
			
		||||
                        var shade = $(this).data('shade');
 | 
			
		||||
                        installNodeModule(id,version,shade,function(xhr) {
 | 
			
		||||
                             if (xhr) {
 | 
			
		||||
                                 if (xhr.responseJSON) {
 | 
			
		||||
                                     RED.notify(RED._('palette.editor.errors.installFailed',{module: id,message:xhr.responseJSON.message}));
 | 
			
		||||
                                 }
 | 
			
		||||
                             }
 | 
			
		||||
                        });
 | 
			
		||||
                        $( this ).dialog( "close" );
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("palette.editor.confirm.button.remove"),
 | 
			
		||||
                    class: "primary palette-module-install-confirm-button-remove",
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        var id = $(this).data('module');
 | 
			
		||||
                        var shade = $(this).data('shade');
 | 
			
		||||
                        shade.show();
 | 
			
		||||
                        removeNodeModule(id, function(xhr) {
 | 
			
		||||
                            shade.hide();
 | 
			
		||||
                            if (xhr) {
 | 
			
		||||
                                if (xhr.responseJSON) {
 | 
			
		||||
                                    RED.notify(RED._('palette.editor.errors.removeFailed',{module: id,message:xhr.responseJSON.message}));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
 | 
			
		||||
                        $( this ).dialog( "close" );
 | 
			
		||||
                        notification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("palette.editor.confirm.button.update"),
 | 
			
		||||
                    class: "primary palette-module-install-confirm-button-update",
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        var id = $(this).data('module');
 | 
			
		||||
                        var version = $(this).data('version');
 | 
			
		||||
                        var shade = $(this).data('shade');
 | 
			
		||||
                        shade.show();
 | 
			
		||||
                        installNodeModule(id,version,shade,function(xhr) {
 | 
			
		||||
                             if (xhr) {
 | 
			
		||||
                                 if (xhr.responseJSON) {
 | 
			
		||||
                                     RED.notify(RED._('palette.editor.errors.updateFailed',{module: id,message:xhr.responseJSON.message}));
 | 
			
		||||
                                 }
 | 
			
		||||
                             }
 | 
			
		||||
                        var spinner = RED.utils.addSpinnerOverlay(container, true);
 | 
			
		||||
                        installNodeModule(entry.name,version,function(xhr) {
 | 
			
		||||
                            spinner.remove();
 | 
			
		||||
                            if (xhr) {
 | 
			
		||||
                                if (xhr.responseJSON) {
 | 
			
		||||
                                    RED.notify(RED._('palette.editor.errors.updateFailed',{module: entry.name,message:xhr.responseJSON.message}));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            done(xhr);
 | 
			
		||||
                        });
 | 
			
		||||
                        $( this ).dialog( "close" );
 | 
			
		||||
                        notification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    function remove(entry,container,done) {
 | 
			
		||||
        if (RED.settings.theme('palette.editable') === false) {
 | 
			
		||||
            done(new Error('Palette not editable'));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        var notification = RED.notify(RED._("palette.editor.confirm.remove.body",{module:entry.name}),{
 | 
			
		||||
            modal: true,
 | 
			
		||||
            fixed: true,
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("common.label.cancel"),
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        notification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: RED._("palette.editor.confirm.button.remove"),
 | 
			
		||||
                    class: "primary palette-module-install-confirm-button-remove",
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        var spinner = RED.utils.addSpinnerOverlay(container, true);
 | 
			
		||||
                        removeNodeModule(entry.name, function(xhr) {
 | 
			
		||||
                            spinner.remove();
 | 
			
		||||
                            if (xhr) {
 | 
			
		||||
                                if (xhr.responseJSON) {
 | 
			
		||||
                                    RED.notify(RED._('palette.editor.errors.removeFailed',{module: entry.name,message:xhr.responseJSON.message}));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        notification.close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    function install(entry,container,done) {
 | 
			
		||||
        if (RED.settings.theme('palette.editable') === false) {
 | 
			
		||||
            done(new Error('Palette not editable'));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        var buttons = [
 | 
			
		||||
            {
 | 
			
		||||
                text: RED._("common.label.cancel"),
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    notification.close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
        if (entry.url) {
 | 
			
		||||
            buttons.push({
 | 
			
		||||
                text: RED._("palette.editor.confirm.button.review"),
 | 
			
		||||
                class: "primary palette-module-install-confirm-button-install",
 | 
			
		||||
                click: function() {
 | 
			
		||||
                    var url = entry.url||"";
 | 
			
		||||
                    window.open(url);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        buttons.push({
 | 
			
		||||
            text: RED._("palette.editor.confirm.button.install"),
 | 
			
		||||
            class: "primary palette-module-install-confirm-button-install",
 | 
			
		||||
            click: function() {
 | 
			
		||||
                var spinner = RED.utils.addSpinnerOverlay(container, true);
 | 
			
		||||
                installNodeModule(entry.id,entry.version,function(xhr) {
 | 
			
		||||
                    spinner.remove();
 | 
			
		||||
                     if (xhr) {
 | 
			
		||||
                         if (xhr.responseJSON) {
 | 
			
		||||
                             RED.notify(RED._('palette.editor.errors.installFailed',{module: entry.id,message:xhr.responseJSON.message}));
 | 
			
		||||
                         }
 | 
			
		||||
                     }
 | 
			
		||||
                     done(xhr);
 | 
			
		||||
                });
 | 
			
		||||
                notification.close();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        var notification = RED.notify(RED._("palette.editor.confirm.install.body",{module:entry.id}),{
 | 
			
		||||
            modal: true,
 | 
			
		||||
            fixed: true,
 | 
			
		||||
            buttons: buttons
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: init
 | 
			
		||||
        init: init,
 | 
			
		||||
        install: install
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,18 @@ RED.palette = (function() {
 | 
			
		||||
 | 
			
		||||
    var categoryContainers = {};
 | 
			
		||||
 | 
			
		||||
    function createCategoryContainer(category, label){
 | 
			
		||||
 | 
			
		||||
    function createCategory(originalCategory,rootCategory,category,ns) {
 | 
			
		||||
        if ($("#palette-base-category-"+rootCategory).length === 0) {
 | 
			
		||||
            createCategoryContainer(originalCategory,rootCategory, ns+":palette.label."+rootCategory);
 | 
			
		||||
        }
 | 
			
		||||
        $("#palette-container-"+rootCategory).show();
 | 
			
		||||
        if ($("#palette-"+category).length === 0) {
 | 
			
		||||
            $("#palette-base-category-"+rootCategory).append('<div id="palette-'+category+'"></div>');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function createCategoryContainer(originalCategory,category, labelId) {
 | 
			
		||||
        var label = RED._(labelId, {defaultValue:category});
 | 
			
		||||
        label = (label || category).replace(/_/g, " ");
 | 
			
		||||
        var catDiv = $('<div id="palette-container-'+category+'" class="palette-category palette-close hide">'+
 | 
			
		||||
            '<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-angle-down"></i><span>'+label+'</span></div>'+
 | 
			
		||||
@@ -31,7 +42,8 @@ RED.palette = (function() {
 | 
			
		||||
            '<div id="palette-'+category+'-function"></div>'+
 | 
			
		||||
            '</div>'+
 | 
			
		||||
            '</div>').appendTo("#palette-container");
 | 
			
		||||
 | 
			
		||||
        catDiv.data('category',originalCategory);
 | 
			
		||||
        catDiv.data('label',label);
 | 
			
		||||
        categoryContainers[category] = {
 | 
			
		||||
            container: catDiv,
 | 
			
		||||
            close: function() {
 | 
			
		||||
@@ -116,6 +128,12 @@ RED.palette = (function() {
 | 
			
		||||
        el.data('popover').setContent(popOverContent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setIcon(element,sf) {
 | 
			
		||||
        var iconElement = element.find(".palette_icon");
 | 
			
		||||
        var icon_url = RED.utils.getNodeIcon(sf._def,sf);
 | 
			
		||||
        iconElement.attr("style", "background-image: url("+icon_url+")");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function escapeNodeType(nt) {
 | 
			
		||||
        return nt.replace(" ","_").replace(".","_").replace(":","_");
 | 
			
		||||
    }
 | 
			
		||||
@@ -127,6 +145,7 @@ RED.palette = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        if (exclusion.indexOf(def.category)===-1) {
 | 
			
		||||
 | 
			
		||||
            var originalCategory = def.category;
 | 
			
		||||
            var category = def.category.replace(/ /g,"_");
 | 
			
		||||
            var rootCategory = category.split("-")[0];
 | 
			
		||||
 | 
			
		||||
@@ -147,14 +166,13 @@ RED.palette = (function() {
 | 
			
		||||
 | 
			
		||||
            d.className="palette_node";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (def.icon) {
 | 
			
		||||
                var icon_url = RED.utils.getNodeIcon(def);
 | 
			
		||||
                var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d);
 | 
			
		||||
                $('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            d.style.backgroundColor = def.color;
 | 
			
		||||
            d.style.backgroundColor = RED.utils.getNodeColor(nt,def);
 | 
			
		||||
 | 
			
		||||
            if (def.outputs > 0) {
 | 
			
		||||
                var portOut = document.createElement("div");
 | 
			
		||||
@@ -168,21 +186,12 @@ RED.palette = (function() {
 | 
			
		||||
                d.appendChild(portIn);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($("#palette-base-category-"+rootCategory).length === 0) {
 | 
			
		||||
                if(coreCategories.indexOf(rootCategory) !== -1){
 | 
			
		||||
                    createCategoryContainer(rootCategory, RED._("node-red:palette.label."+rootCategory, {defaultValue:rootCategory}));
 | 
			
		||||
                } else {
 | 
			
		||||
                    var ns = def.set.id;
 | 
			
		||||
                    createCategoryContainer(rootCategory, RED._(ns+":palette.label."+rootCategory, {defaultValue:rootCategory}));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $("#palette-container-"+rootCategory).show();
 | 
			
		||||
 | 
			
		||||
            if ($("#palette-"+category).length === 0) {
 | 
			
		||||
                $("#palette-base-category-"+rootCategory).append('<div id="palette-'+category+'"></div>');
 | 
			
		||||
            }
 | 
			
		||||
            createCategory(def.category,rootCategory,category,(coreCategories.indexOf(rootCategory) !== -1)?"node-red":def.set.id);
 | 
			
		||||
 | 
			
		||||
            $("#palette-"+category).append(d);
 | 
			
		||||
 | 
			
		||||
            $(d).data('category',rootCategory);
 | 
			
		||||
 | 
			
		||||
            d.onmousedown = function(e) { e.preventDefault(); };
 | 
			
		||||
 | 
			
		||||
            var popover = RED.popover.create({
 | 
			
		||||
@@ -206,11 +215,11 @@ RED.palette = (function() {
 | 
			
		||||
                RED.view.focus();
 | 
			
		||||
                var helpText;
 | 
			
		||||
                if (nt.indexOf("subflow:") === 0) {
 | 
			
		||||
                    helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"");
 | 
			
		||||
                    helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
 | 
			
		||||
                } else {
 | 
			
		||||
                    helpText = $("script[data-help-name='"+d.type+"']").html()||"";
 | 
			
		||||
                    helpText = $("script[data-help-name='"+d.type+"']").html()||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
 | 
			
		||||
                }
 | 
			
		||||
                RED.sidebar.info.set(helpText);
 | 
			
		||||
                RED.sidebar.info.set(helpText,RED._("sidebar.info.nodeHelp"));
 | 
			
		||||
            });
 | 
			
		||||
            var chart = $("#chart");
 | 
			
		||||
            var chartOffset = chart.offset();
 | 
			
		||||
@@ -219,13 +228,19 @@ RED.palette = (function() {
 | 
			
		||||
            var mouseX;
 | 
			
		||||
            var mouseY;
 | 
			
		||||
            var spliceTimer;
 | 
			
		||||
            var paletteWidth;
 | 
			
		||||
            var paletteTop;
 | 
			
		||||
            $(d).draggable({
 | 
			
		||||
                helper: 'clone',
 | 
			
		||||
                appendTo: 'body',
 | 
			
		||||
                revert: true,
 | 
			
		||||
                revertDuration: 50,
 | 
			
		||||
                containment:'#main-container',
 | 
			
		||||
                start: function() {RED.view.focus();},
 | 
			
		||||
                start: function() {
 | 
			
		||||
                    paletteWidth = $("#palette").width();
 | 
			
		||||
                    paletteTop = $("#palette").parent().position().top + $("#palette-container").position().top;
 | 
			
		||||
                    RED.view.focus();
 | 
			
		||||
                },
 | 
			
		||||
                stop: function() { d3.select('.link_splice').classed('link_splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}},
 | 
			
		||||
                drag: function(e,ui) {
 | 
			
		||||
 | 
			
		||||
@@ -235,9 +250,8 @@ RED.palette = (function() {
 | 
			
		||||
                    ui.position.left += 17.5;
 | 
			
		||||
 | 
			
		||||
                    if (def.inputs > 0 && def.outputs > 0) {
 | 
			
		||||
                        mouseX = ui.position.left+(ui.helper.width()/2) - chartOffset.left + chart.scrollLeft();
 | 
			
		||||
                        mouseY = ui.position.top+(ui.helper.height()/2) - chartOffset.top + chart.scrollTop();
 | 
			
		||||
 | 
			
		||||
                        mouseX = ui.position.left-paletteWidth+(ui.helper.width()/2) - chartOffset.left + chart.scrollLeft();
 | 
			
		||||
                        mouseY = ui.position.top-paletteTop+(ui.helper.height()/2) - chartOffset.top + chart.scrollTop();
 | 
			
		||||
                        if (!spliceTimer) {
 | 
			
		||||
                            spliceTimer = setTimeout(function() {
 | 
			
		||||
                                var nodes = [];
 | 
			
		||||
@@ -259,6 +273,7 @@ RED.palette = (function() {
 | 
			
		||||
                                    mouseY /= RED.view.scale();
 | 
			
		||||
                                    nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                for (var i=0;i<nodes.length;i++) {
 | 
			
		||||
                                    if (d3.select(nodes[i]).classed('link_background')) {
 | 
			
		||||
                                        var length = nodes[i].getTotalLength();
 | 
			
		||||
@@ -296,7 +311,7 @@ RED.palette = (function() {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            var nodeInfo = null;
 | 
			
		||||
            if (def.category == "subflows") {
 | 
			
		||||
            if (nt.indexOf("subflow:") === 0) {
 | 
			
		||||
                $(d).dblclick(function(e) {
 | 
			
		||||
                    RED.workspaces.show(nt.substring(8));
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
@@ -325,14 +340,26 @@ RED.palette = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function hideNodeType(nt) {
 | 
			
		||||
        var nodeTypeId = escapeNodeType(nt);
 | 
			
		||||
        $("#palette_node_"+nodeTypeId).hide();
 | 
			
		||||
        var paletteNode = $("#palette_node_"+nodeTypeId);
 | 
			
		||||
        paletteNode.hide();
 | 
			
		||||
        var categoryNode = paletteNode.closest(".palette-category");
 | 
			
		||||
        var cl = categoryNode.find(".palette_node");
 | 
			
		||||
        var c = 0;
 | 
			
		||||
        for (var i = 0; i < cl.length; i++) {
 | 
			
		||||
            if ($(cl[i]).css('display') === 'none') { c += 1; }
 | 
			
		||||
        }
 | 
			
		||||
        if (c === cl.length) { categoryNode.hide(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showNodeType(nt) {
 | 
			
		||||
        var nodeTypeId = escapeNodeType(nt);
 | 
			
		||||
        $("#palette_node_"+nodeTypeId).show();
 | 
			
		||||
        var paletteNode = $("#palette_node_"+nodeTypeId);
 | 
			
		||||
        var categoryNode = paletteNode.closest(".palette-category");
 | 
			
		||||
        categoryNode.show();
 | 
			
		||||
        paletteNode.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function refreshNodeTypes() {
 | 
			
		||||
@@ -357,6 +384,32 @@ RED.palette = (function() {
 | 
			
		||||
                portOutput.remove();
 | 
			
		||||
            }
 | 
			
		||||
            setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||""));
 | 
			
		||||
            setIcon(paletteNode,sf);
 | 
			
		||||
 | 
			
		||||
            var currentCategory = paletteNode.data('category');
 | 
			
		||||
            var newCategory = (sf.category||"subflows");
 | 
			
		||||
            if (currentCategory !== newCategory) {
 | 
			
		||||
                var category = newCategory.replace(/ /g,"_");
 | 
			
		||||
                createCategory(newCategory,category,category,"node-red");
 | 
			
		||||
 | 
			
		||||
                var currentCategoryNode = paletteNode.closest(".palette-category");
 | 
			
		||||
                var newCategoryNode = $("#palette-"+category);
 | 
			
		||||
                newCategoryNode.append(paletteNode);
 | 
			
		||||
                if (newCategoryNode.find(".palette_node").length === 1) {
 | 
			
		||||
                    categoryContainers[category].open();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                paletteNode.data('category',newCategory);
 | 
			
		||||
                if (currentCategoryNode.find(".palette_node").length === 0) {
 | 
			
		||||
                    if (currentCategoryNode.find("i").hasClass("expanded")) {
 | 
			
		||||
                        currentCategoryNode.find(".palette-content").slideToggle();
 | 
			
		||||
                        currentCategoryNode.find("i").toggleClass("expanded");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -396,21 +449,21 @@ RED.palette = (function() {
 | 
			
		||||
        RED.events.on('registry:node-type-removed', function(nodeType) {
 | 
			
		||||
            removeNodeType(nodeType);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.events.on('registry:node-set-enabled', function(nodeSet) {
 | 
			
		||||
            for (var j=0;j<nodeSet.types.length;j++) {
 | 
			
		||||
                showNodeType(nodeSet.types[j]);
 | 
			
		||||
                var def = RED.nodes.getType(nodeSet.types[j]);
 | 
			
		||||
                if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
 | 
			
		||||
                if (def && def.onpaletteadd && typeof def.onpaletteadd === "function") {
 | 
			
		||||
                    def.onpaletteadd.call(def);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        RED.events.on('registry:node-set-disabled', function(nodeSet) {
 | 
			
		||||
            console.log(nodeSet);
 | 
			
		||||
            for (var j=0;j<nodeSet.types.length;j++) {
 | 
			
		||||
                hideNodeType(nodeSet.types[j]);
 | 
			
		||||
                var def = RED.nodes.getType(nodeSet.types[j]);
 | 
			
		||||
                if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
 | 
			
		||||
                if (def && def.onpaletteremove && typeof def.onpaletteremove === "function") {
 | 
			
		||||
                    def.onpaletteremove.call(def);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -420,14 +473,13 @@ RED.palette = (function() {
 | 
			
		||||
                for (var j=0;j<nodeSet.types.length;j++) {
 | 
			
		||||
                    removeNodeType(nodeSet.types[j]);
 | 
			
		||||
                    var def = RED.nodes.getType(nodeSet.types[j]);
 | 
			
		||||
                    if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
 | 
			
		||||
                    if (def && def.onpaletteremove && typeof def.onpaletteremove === "function") {
 | 
			
		||||
                        def.onpaletteremove.call(def);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $("#palette > .palette-spinner").show();
 | 
			
		||||
 | 
			
		||||
        $("#palette-search input").searchBox({
 | 
			
		||||
@@ -447,7 +499,7 @@ RED.palette = (function() {
 | 
			
		||||
            categoryList = coreCategories
 | 
			
		||||
        }
 | 
			
		||||
        categoryList.forEach(function(category){
 | 
			
		||||
            createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category}));
 | 
			
		||||
            createCategoryContainer(category, category, "palette.label."+category);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $("#palette-collapse-all").on("click", function(e) {
 | 
			
		||||
@@ -467,13 +519,20 @@ RED.palette = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getCategories() {
 | 
			
		||||
        var categories = [];
 | 
			
		||||
        $("#palette-container .palette-category").each(function(i,d) {
 | 
			
		||||
            categories.push({id:$(d).data('category'),label:$(d).data('label')});
 | 
			
		||||
        })
 | 
			
		||||
        return categories;
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
        init: init,
 | 
			
		||||
        add:addNodeType,
 | 
			
		||||
        remove:removeNodeType,
 | 
			
		||||
        hide:hideNodeType,
 | 
			
		||||
        show:showNodeType,
 | 
			
		||||
        refresh:refreshNodeTypes
 | 
			
		||||
        refresh:refreshNodeTypes,
 | 
			
		||||
        getCategories: getCategories
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1535
									
								
								editor/js/ui/projects/projectSettings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1535
									
								
								editor/js/ui/projects/projectSettings.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										418
									
								
								editor/js/ui/projects/projectUserSettings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								editor/js/ui/projects/projectUserSettings.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,418 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
RED.projects.userSettings = (function() {
 | 
			
		||||
 | 
			
		||||
    var gitUsernameInput;
 | 
			
		||||
    var gitEmailInput;
 | 
			
		||||
 | 
			
		||||
    function createGitUserSection(pane) {
 | 
			
		||||
 | 
			
		||||
        var currentGitSettings = RED.settings.get('git') || {};
 | 
			
		||||
        currentGitSettings.user = currentGitSettings.user || {};
 | 
			
		||||
 | 
			
		||||
        var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.committerDetail")).appendTo(pane);
 | 
			
		||||
 | 
			
		||||
        var gitconfigContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
 | 
			
		||||
        $('<div style="color:#aaa;"></div>').appendTo(gitconfigContainer).text(RED._("editor:sidebar.project.userSettings.committerTip"));
 | 
			
		||||
 | 
			
		||||
        var row = $('<div class="user-settings-row"></div>').appendTo(gitconfigContainer);
 | 
			
		||||
        $('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.userName")).appendTo(row);
 | 
			
		||||
        gitUsernameInput = $('<input type="text">').appendTo(row);
 | 
			
		||||
        gitUsernameInput.val(currentGitSettings.user.name||"");
 | 
			
		||||
 | 
			
		||||
        row = $('<div class="user-settings-row"></div>').appendTo(gitconfigContainer);
 | 
			
		||||
        $('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row);
 | 
			
		||||
        gitEmailInput = $('<input type="text">').appendTo(row);
 | 
			
		||||
        gitEmailInput.val(currentGitSettings.user.email||"");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function createSSHKeySection(pane) {
 | 
			
		||||
        var container = $('<div class="user-settings-section"></div>').appendTo(pane);
 | 
			
		||||
        var popover;
 | 
			
		||||
        var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(container);
 | 
			
		||||
        var subtitle = $('<div style="color:#aaa;"></div>').appendTo(container).text(RED._("editor:sidebar.project.userSettings.sshKeysTip"));
 | 
			
		||||
 | 
			
		||||
        var addKeyButton = $('<button id="user-settings-gitconfig-add-key" class="editor-button editor-button-small" style="float: right; margin-right: 10px;">'+RED._("editor:sidebar.project.userSettings.add")+'</button>')
 | 
			
		||||
            .appendTo(subtitle)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                addKeyButton.attr('disabled',true);
 | 
			
		||||
                saveButton.attr('disabled',true);
 | 
			
		||||
                // bg.children().removeClass("selected");
 | 
			
		||||
                // addLocalButton.click();
 | 
			
		||||
                addKeyDialog.slideDown(200);
 | 
			
		||||
                keyNameInput.focus();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        var validateForm = function() {
 | 
			
		||||
            var valid = /^[a-zA-Z0-9\-_]+$/.test(keyNameInput.val());
 | 
			
		||||
            keyNameInput.toggleClass('input-error',keyNameInputChanged&&!valid);
 | 
			
		||||
 | 
			
		||||
            // var selectedButton = bg.find(".selected");
 | 
			
		||||
            // if (selectedButton[0] === addLocalButton[0]) {
 | 
			
		||||
            //     valid = valid && localPublicKeyPathInput.val().length > 0 && localPrivateKeyPathInput.val().length > 0;
 | 
			
		||||
            // } else if (selectedButton[0] === uploadButton[0]) {
 | 
			
		||||
            //     valid = valid && publicKeyInput.val().length > 0 && privateKeyInput.val().length > 0;
 | 
			
		||||
            // } else if (selectedButton[0] === generateButton[0]) {
 | 
			
		||||
                var passphrase = passphraseInput.val();
 | 
			
		||||
                var validPassphrase = passphrase.length === 0 || passphrase.length >= 8;
 | 
			
		||||
                passphraseInput.toggleClass('input-error',!validPassphrase);
 | 
			
		||||
                if (!validPassphrase) {
 | 
			
		||||
                    passphraseInputSubLabel.text(RED._("editor:sidebar.project.userSettings.passphraseShort"));
 | 
			
		||||
                } else if (passphrase.length === 0) {
 | 
			
		||||
                    passphraseInputSubLabel.text(RED._("editor:sidebar.project.userSettings.optional"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    passphraseInputSubLabel.text("");
 | 
			
		||||
                }
 | 
			
		||||
                valid = valid && validPassphrase;
 | 
			
		||||
            // }
 | 
			
		||||
 | 
			
		||||
            saveButton.attr('disabled',!valid);
 | 
			
		||||
 | 
			
		||||
            if (popover) {
 | 
			
		||||
                popover.close();
 | 
			
		||||
                popover = null;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var row = $('<div class="user-settings-row"></div>').appendTo(container);
 | 
			
		||||
        var addKeyDialog = $('<div class="projects-dialog-list-dialog"></div>').hide().appendTo(row);
 | 
			
		||||
        $('<div class="projects-dialog-list-dialog-header">').text(RED._("editor:sidebar.project.userSettings.addSshKey")).appendTo(addKeyDialog);
 | 
			
		||||
        var addKeyDialogBody = $('<div>').appendTo(addKeyDialog);
 | 
			
		||||
 | 
			
		||||
        row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialogBody);
 | 
			
		||||
        $('<div style="color:#aaa;"></div>').appendTo(row).text(RED._("editor:sidebar.project.userSettings.addSshKeyTip"));
 | 
			
		||||
        // var bg = $('<div></div>',{class:"button-group", style:"text-align: center"}).appendTo(row);
 | 
			
		||||
        // var addLocalButton = $('<button class="editor-button toggle selected">use local key</button>').appendTo(bg);
 | 
			
		||||
        // var uploadButton = $('<button class="editor-button toggle">upload key</button>').appendTo(bg);
 | 
			
		||||
        // var generateButton = $('<button class="editor-button toggle">generate key</button>').appendTo(bg);
 | 
			
		||||
        // bg.children().click(function(e) {
 | 
			
		||||
        //     e.preventDefault();
 | 
			
		||||
        //     if ($(this).hasClass("selected")) {
 | 
			
		||||
        //         return;
 | 
			
		||||
        //     }
 | 
			
		||||
        //     bg.children().removeClass("selected");
 | 
			
		||||
        //     $(this).addClass("selected");
 | 
			
		||||
        //     if (this === addLocalButton[0]) {
 | 
			
		||||
        //         addLocalKeyPane.show();
 | 
			
		||||
        //         generateKeyPane.hide();
 | 
			
		||||
        //         uploadKeyPane.hide();
 | 
			
		||||
        //     } else if (this === uploadButton[0]) {
 | 
			
		||||
        //         addLocalKeyPane.hide();
 | 
			
		||||
        //         generateKeyPane.hide();
 | 
			
		||||
        //         uploadKeyPane.show();
 | 
			
		||||
        //     } else if (this === generateButton[0]){
 | 
			
		||||
        //         addLocalKeyPane.hide();
 | 
			
		||||
        //         generateKeyPane.show();
 | 
			
		||||
        //         uploadKeyPane.hide();
 | 
			
		||||
        //     }
 | 
			
		||||
        //     validateForm();
 | 
			
		||||
        // })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialogBody);
 | 
			
		||||
        $('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.name")).appendTo(row);
 | 
			
		||||
        var keyNameInputChanged = false;
 | 
			
		||||
        var keyNameInput = $('<input type="text">').appendTo(row).on("change keyup paste",function() {
 | 
			
		||||
            keyNameInputChanged = true;
 | 
			
		||||
            validateForm();
 | 
			
		||||
        });
 | 
			
		||||
        $('<label class="projects-edit-form-sublabel"><small>'+RED._("editor:sidebar.project.userSettings.nameRule")+'</small></label>').appendTo(row).find("small");
 | 
			
		||||
 | 
			
		||||
        var generateKeyPane = $('<div>').appendTo(addKeyDialogBody);
 | 
			
		||||
        row = $('<div class="user-settings-row"></div>').appendTo(generateKeyPane);
 | 
			
		||||
        $('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.passphrase")).appendTo(row);
 | 
			
		||||
        var passphraseInput = $('<input type="password">').appendTo(row).on("change keyup paste",validateForm);
 | 
			
		||||
        var passphraseInputSubLabel = $('<label class="projects-edit-form-sublabel"><small>'+RED._("editor:sidebar.project.userSettings.optional")+'</small></label>').appendTo(row).find("small");
 | 
			
		||||
 | 
			
		||||
        // var addLocalKeyPane = $('<div>').hide().appendTo(addKeyDialogBody);
 | 
			
		||||
        // row = $('<div class="user-settings-row"></div>').appendTo(addLocalKeyPane);
 | 
			
		||||
        // $('<label for=""></label>').text('Public key').appendTo(row);
 | 
			
		||||
        // var localPublicKeyPathInput = $('<input type="text">').appendTo(row).on("change keyup paste",validateForm);
 | 
			
		||||
        // $('<label class="projects-edit-form-sublabel"><small>Public key file path, for example: ~/.ssh/id_rsa.pub</small></label>').appendTo(row).find("small");
 | 
			
		||||
        // row = $('<div class="user-settings-row"></div>').appendTo(addLocalKeyPane);
 | 
			
		||||
        // $('<label for=""></label>').text('Private key').appendTo(row);
 | 
			
		||||
        // var localPrivateKeyPathInput = $('<input type="text">').appendTo(row).on("change keyup paste",validateForm);
 | 
			
		||||
        // $('<label class="projects-edit-form-sublabel"><small>Private key file path, for example: ~/.ssh/id_rsa</small></label>').appendTo(row).find("small");
 | 
			
		||||
        //
 | 
			
		||||
        // var uploadKeyPane = $('<div>').hide().appendTo(addKeyDialogBody);
 | 
			
		||||
        // row = $('<div class="user-settings-row"></div>').appendTo(uploadKeyPane);
 | 
			
		||||
        // $('<label for=""></label>').text('Public key').appendTo(row);
 | 
			
		||||
        // var publicKeyInput = $('<textarea>').appendTo(row).on("change keyup paste",validateForm);
 | 
			
		||||
        // $('<label class="projects-edit-form-sublabel"><small>Paste in public key contents, for example: ~/.ssh/id_rsa.pub</small></label>').appendTo(row).find("small");
 | 
			
		||||
        // row = $('<div class="user-settings-row"></div>').appendTo(uploadKeyPane);
 | 
			
		||||
        // $('<label for=""></label>').text('Private key').appendTo(row);
 | 
			
		||||
        // var privateKeyInput = $('<textarea>').appendTo(row).on("change keyup paste",validateForm);
 | 
			
		||||
        // $('<label class="projects-edit-form-sublabel"><small>Paste in private key contents, for example: ~/.ssh/id_rsa</small></label>').appendTo(row).find("small");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var hideEditForm = function() {
 | 
			
		||||
            addKeyButton.attr('disabled',false);
 | 
			
		||||
            addKeyDialog.hide();
 | 
			
		||||
 | 
			
		||||
            keyNameInput.val("");
 | 
			
		||||
            keyNameInputChanged = false;
 | 
			
		||||
            passphraseInput.val("");
 | 
			
		||||
            // localPublicKeyPathInput.val("");
 | 
			
		||||
            // localPrivateKeyPathInput.val("");
 | 
			
		||||
            // publicKeyInput.val("");
 | 
			
		||||
            // privateKeyInput.val("");
 | 
			
		||||
            if (popover) {
 | 
			
		||||
                popover.close();
 | 
			
		||||
                popover = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        var formButtons = $('<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>').appendTo(addKeyDialog);
 | 
			
		||||
        $('<button class="editor-button">'+RED._("editor:sidebar.project.userSettings.cancel")+'</button>')
 | 
			
		||||
            .appendTo(formButtons)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                hideEditForm();
 | 
			
		||||
            });
 | 
			
		||||
        var saveButton = $('<button class="editor-button">'+RED._("editor:sidebar.project.userSettings.generate")+'</button>')
 | 
			
		||||
            .appendTo(formButtons)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                var spinner = utils.addSpinnerOverlay(addKeyDialog).addClass('projects-dialog-spinner-contain');
 | 
			
		||||
                var payload = {
 | 
			
		||||
                    name: keyNameInput.val()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                // var selectedButton = bg.find(".selected");
 | 
			
		||||
                // if (selectedButton[0] === addLocalButton[0]) {
 | 
			
		||||
                //     payload.type = "local";
 | 
			
		||||
                //     payload.publicKeyPath = localPublicKeyPathInput.val();
 | 
			
		||||
                //     payload.privateKeyPath = localPrivateKeyPathInput.val();
 | 
			
		||||
                // } else if (selectedButton[0] === uploadButton[0]) {
 | 
			
		||||
                //     payload.type = "upload";
 | 
			
		||||
                //     payload.publicKey = publicKeyInput.val();
 | 
			
		||||
                //     payload.privateKey = privateKeyInput.val();
 | 
			
		||||
                // } else if (selectedButton[0] === generateButton[0]) {
 | 
			
		||||
                    payload.type = "generate";
 | 
			
		||||
                    payload.comment = gitEmailInput.val();
 | 
			
		||||
                    payload.password = passphraseInput.val();
 | 
			
		||||
                    payload.size = 4096;
 | 
			
		||||
                // }
 | 
			
		||||
                var done = function(err) {
 | 
			
		||||
                    spinner.remove();
 | 
			
		||||
                    if (err) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    hideEditForm();
 | 
			
		||||
                }
 | 
			
		||||
                // console.log(JSON.stringify(payload,null,4));
 | 
			
		||||
                RED.deploy.setDeployInflight(true);
 | 
			
		||||
                utils.sendRequest({
 | 
			
		||||
                    url: "settings/user/keys",
 | 
			
		||||
                    type: "POST",
 | 
			
		||||
                    responses: {
 | 
			
		||||
                        0: function(error) {
 | 
			
		||||
                            done(error);
 | 
			
		||||
                        },
 | 
			
		||||
                        200: function(data) {
 | 
			
		||||
                            refreshSSHKeyList(payload.name);
 | 
			
		||||
                            done();
 | 
			
		||||
                        },
 | 
			
		||||
                        400: {
 | 
			
		||||
                            'unexpected_error': function(error) {
 | 
			
		||||
                                console.log(error);
 | 
			
		||||
                                done(error);
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                },payload);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        row = $('<div class="user-settings-row projects-dialog-list"></div>').appendTo(container);
 | 
			
		||||
        var emptyItem = { empty: true };
 | 
			
		||||
        var expandKey = function(container,entry) {
 | 
			
		||||
            var row = $('<div class="projects-dialog-ssh-public-key">',{style:"position:relative"}).appendTo(container);
 | 
			
		||||
            var keyBox = $('<pre>',{style:"min-height: 80px"}).appendTo(row);
 | 
			
		||||
            var spinner = utils.addSpinnerOverlay(keyBox).addClass('projects-dialog-spinner-contain');
 | 
			
		||||
            var options = {
 | 
			
		||||
                url: 'settings/user/keys/'+entry.name,
 | 
			
		||||
                type: "GET",
 | 
			
		||||
                responses: {
 | 
			
		||||
                    200: function(data) {
 | 
			
		||||
                        keyBox.text(data.publickey);
 | 
			
		||||
                        spinner.remove();
 | 
			
		||||
                    },
 | 
			
		||||
                    400: {
 | 
			
		||||
                        'unexpected_error': function(error) {
 | 
			
		||||
                            console.log(error);
 | 
			
		||||
                            spinner.remove();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            utils.sendRequest(options);
 | 
			
		||||
 | 
			
		||||
            var formButtons = $('<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>').appendTo(row);
 | 
			
		||||
            $('<button class="editor-button editor-button-small">'+RED._("editor:sidebar.project.userSettings.copyPublicKey")+'</button>')
 | 
			
		||||
                .appendTo(formButtons)
 | 
			
		||||
                .click(function(evt) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        evt.stopPropagation();
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
                        document.getSelection().selectAllChildren(keyBox[0]);
 | 
			
		||||
                        var ret = document.execCommand('copy');
 | 
			
		||||
                        document.getSelection().empty();
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            return row;
 | 
			
		||||
        }
 | 
			
		||||
        var keyList = $('<ol class="projects-dialog-ssh-key-list">').appendTo(row).editableList({
 | 
			
		||||
            height: 'auto',
 | 
			
		||||
            addButton: false,
 | 
			
		||||
            scrollOnAdd: false,
 | 
			
		||||
            addItem: function(row,index,entry) {
 | 
			
		||||
                var container = $('<div class="projects-dialog-list-entry">').appendTo(row);
 | 
			
		||||
                if (entry.empty) {
 | 
			
		||||
                    container.addClass('red-ui-search-empty');
 | 
			
		||||
                    container.text(RED._("editor:sidebar.project.userSettings.noSshKeys"));
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                var topRow = $('<div class="projects-dialog-ssh-key-header">').appendTo(container);
 | 
			
		||||
                $('<span class="entry-icon"><i class="fa fa-key"></i></span>').appendTo(topRow);
 | 
			
		||||
                $('<span class="entry-name">').text(entry.name).appendTo(topRow);
 | 
			
		||||
                var tools = $('<span class="button-row entry-tools">').appendTo(topRow);
 | 
			
		||||
                var expandedRow;
 | 
			
		||||
                topRow.click(function(e) {
 | 
			
		||||
                    if (expandedRow) {
 | 
			
		||||
                        expandedRow.slideUp(200,function() {
 | 
			
		||||
                            expandedRow.remove();
 | 
			
		||||
                            expandedRow = null;
 | 
			
		||||
                        })
 | 
			
		||||
                    } else {
 | 
			
		||||
                        expandedRow = expandKey(container,entry);
 | 
			
		||||
                    }
 | 
			
		||||
                    })
 | 
			
		||||
                if (!entry.system) {
 | 
			
		||||
                    $('<button class="editor-button editor-button-small"><i class="fa fa-trash"></i></button>')
 | 
			
		||||
                        .appendTo(tools)
 | 
			
		||||
                        .click(function(e) {
 | 
			
		||||
                            e.stopPropagation();
 | 
			
		||||
                            var spinner = utils.addSpinnerOverlay(row).addClass('projects-dialog-spinner-contain');
 | 
			
		||||
                            var notification = RED.notify(RED._("editor:sidebar.project.userSettings.deleteConfirm", {name:entry.name}), {
 | 
			
		||||
                                type: 'warning',
 | 
			
		||||
                                modal: true,
 | 
			
		||||
                                fixed: true,
 | 
			
		||||
                                buttons: [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        text: RED._("common.label.cancel"),
 | 
			
		||||
                                        click: function() {
 | 
			
		||||
                                            spinner.remove();
 | 
			
		||||
                                            notification.close();
 | 
			
		||||
                                        }
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        text: RED._("editor:sidebar.project.userSettings.delete"),
 | 
			
		||||
                                        click: function() {
 | 
			
		||||
                                            notification.close();
 | 
			
		||||
                                            var url = "settings/user/keys/"+entry.name;
 | 
			
		||||
                                            var options = {
 | 
			
		||||
                                                url: url,
 | 
			
		||||
                                                type: "DELETE",
 | 
			
		||||
                                                responses: {
 | 
			
		||||
                                                    200: function(data) {
 | 
			
		||||
                                                        row.fadeOut(200,function() {
 | 
			
		||||
                                                            keyList.editableList('removeItem',entry);
 | 
			
		||||
                                                            setTimeout(spinner.remove, 100);
 | 
			
		||||
                                                            if (keyList.editableList('length') === 0) {
 | 
			
		||||
                                                                keyList.editableList('addItem',emptyItem);
 | 
			
		||||
                                                            }
 | 
			
		||||
                                                        });
 | 
			
		||||
                                                    },
 | 
			
		||||
                                                    400: {
 | 
			
		||||
                                                        'unexpected_error': function(error) {
 | 
			
		||||
                                                            console.log(error);
 | 
			
		||||
                                                            spinner.remove();
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    },
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                            utils.sendRequest(options);
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
                if (entry.expand) {
 | 
			
		||||
                    expandedRow = expandKey(container,entry);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        var refreshSSHKeyList = function(justAdded) {
 | 
			
		||||
            $.getJSON("settings/user/keys",function(result) {
 | 
			
		||||
                if (result.keys) {
 | 
			
		||||
                    result.keys.sort(function(A,B) {
 | 
			
		||||
                        return A.name.localeCompare(B.name);
 | 
			
		||||
                    });
 | 
			
		||||
                    keyList.editableList('empty');
 | 
			
		||||
                    result.keys.forEach(function(key) {
 | 
			
		||||
                        if (key.name === justAdded) {
 | 
			
		||||
                            key.expand = true;
 | 
			
		||||
                        }
 | 
			
		||||
                        keyList.editableList('addItem',key);
 | 
			
		||||
                    });
 | 
			
		||||
                    if (keyList.editableList('length') === 0) {
 | 
			
		||||
                        keyList.editableList('addItem',emptyItem);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        refreshSSHKeyList();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function createSettingsPane(activeProject) {
 | 
			
		||||
        var pane = $('<div id="user-settings-tab-gitconfig" class="project-settings-tab-pane node-help"></div>');
 | 
			
		||||
        createGitUserSection(pane);
 | 
			
		||||
        createSSHKeySection(pane);
 | 
			
		||||
        return pane;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var utils;
 | 
			
		||||
    function init(_utils) {
 | 
			
		||||
        utils = _utils;
 | 
			
		||||
        RED.userSettings.add({
 | 
			
		||||
            id:'gitconfig',
 | 
			
		||||
            title: RED._("editor:sidebar.project.userSettings.gitConfig"),
 | 
			
		||||
            get: createSettingsPane,
 | 
			
		||||
            close: function() {
 | 
			
		||||
                var currentGitSettings = RED.settings.get('git') || {};
 | 
			
		||||
                currentGitSettings.user = currentGitSettings.user || {};
 | 
			
		||||
                currentGitSettings.user.name = gitUsernameInput.val();
 | 
			
		||||
                currentGitSettings.user.email = gitEmailInput.val();
 | 
			
		||||
                RED.settings.set('git', currentGitSettings);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: init,
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										2377
									
								
								editor/js/ui/projects/projects.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2377
									
								
								editor/js/ui/projects/projects.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1374
									
								
								editor/js/ui/projects/tab-versionControl.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1374
									
								
								editor/js/ui/projects/tab-versionControl.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -26,6 +26,24 @@ RED.search = (function() {
 | 
			
		||||
    var keys = [];
 | 
			
		||||
    var results = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function indexProperty(node,label,property) {
 | 
			
		||||
        if (typeof property === 'string' || typeof property === 'number') {
 | 
			
		||||
            property = (""+property).toLowerCase();
 | 
			
		||||
            index[property] = index[property] || {};
 | 
			
		||||
            index[property][node.id] = {node:node,label:label};
 | 
			
		||||
        } else if (Array.isArray(property)) {
 | 
			
		||||
            property.forEach(function(prop) {
 | 
			
		||||
                indexProperty(node,label,prop);
 | 
			
		||||
            })
 | 
			
		||||
        } else if (typeof property === 'object') {
 | 
			
		||||
            for (var prop in property) {
 | 
			
		||||
                if (property.hasOwnProperty(prop)) {
 | 
			
		||||
                    indexProperty(node,label,property[prop])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function indexNode(n) {
 | 
			
		||||
        var l = RED.utils.getNodeLabel(n);
 | 
			
		||||
        if (l) {
 | 
			
		||||
@@ -42,17 +60,11 @@ RED.search = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        for (var i=0;i<properties.length;i++) {
 | 
			
		||||
            if (n.hasOwnProperty(properties[i])) {
 | 
			
		||||
                var v = n[properties[i]];
 | 
			
		||||
                if (typeof v === 'string' || typeof v === 'number') {
 | 
			
		||||
                    v = (""+v).toLowerCase();
 | 
			
		||||
                    index[v] = index[v] || {};
 | 
			
		||||
                    index[v][n.id] = {node:n,label:l};
 | 
			
		||||
                }
 | 
			
		||||
                indexProperty(n, l, n[properties[i]]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function indexWorkspace() {
 | 
			
		||||
        index = {};
 | 
			
		||||
        RED.nodes.eachWorkspace(indexNode);
 | 
			
		||||
@@ -125,7 +137,7 @@ RED.search = (function() {
 | 
			
		||||
    function createDialog() {
 | 
			
		||||
        dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#main-container");
 | 
			
		||||
        var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
 | 
			
		||||
        searchInput = $('<input type="text" placeholder="search your flows">').appendTo(searchDiv).searchBox({
 | 
			
		||||
        searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.searchInput">').appendTo(searchDiv).searchBox({
 | 
			
		||||
            delay: 200,
 | 
			
		||||
            change: function() {
 | 
			
		||||
                search($(this).val());
 | 
			
		||||
@@ -166,6 +178,7 @@ RED.search = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        searchInput.i18n();
 | 
			
		||||
 | 
			
		||||
        var searchResultsDiv = $("<div>",{class:"red-ui-search-results-container"}).appendTo(dialog);
 | 
			
		||||
        searchResults = $('<ol>',{id:"search-result-list", style:"position: absolute;top: 5px;bottom: 5px;left: 5px;right: 5px;"}).appendTo(searchResultsDiv).editableList({
 | 
			
		||||
@@ -173,14 +186,14 @@ RED.search = (function() {
 | 
			
		||||
            addItem: function(container,i,object) {
 | 
			
		||||
                var node = object.node;
 | 
			
		||||
                if (node === undefined) {
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).html(RED._('search.empty')).appendTo(container);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    var def = node._def;
 | 
			
		||||
                    var div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
 | 
			
		||||
 | 
			
		||||
                    var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
 | 
			
		||||
                    var colour = def.color;
 | 
			
		||||
                    var colour = RED.utils.getNodeColor(node.type,def);
 | 
			
		||||
                    var icon_url = RED.utils.getNodeIcon(def,node);
 | 
			
		||||
                    if (node.type === 'tab') {
 | 
			
		||||
                        colour = "#C0DEED";
 | 
			
		||||
@@ -199,12 +212,12 @@ RED.search = (function() {
 | 
			
		||||
                        } else {
 | 
			
		||||
                            workspace = "flow:"+workspace.label;
 | 
			
		||||
                        }
 | 
			
		||||
                        $('<div>',{class:"red-ui-search-result-node-flow"}).html(workspace).appendTo(contentDiv);
 | 
			
		||||
                        $('<div>',{class:"red-ui-search-result-node-flow"}).text(workspace).appendTo(contentDiv);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-label"}).html(object.label || node.id).appendTo(contentDiv);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-type"}).html(node.type).appendTo(contentDiv);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-id"}).html(node.id).appendTo(contentDiv);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-label"}).text(object.label || node.id).appendTo(contentDiv);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-type"}).text(node.type).appendTo(contentDiv);
 | 
			
		||||
                    $('<div>',{class:"red-ui-search-result-node-id"}).text(node.id).appendTo(contentDiv);
 | 
			
		||||
 | 
			
		||||
                    div.click(function(evt) {
 | 
			
		||||
                        evt.preventDefault();
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,9 @@ RED.sidebar = (function() {
 | 
			
		||||
                tab.onremove.call(tab);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        minimumActiveTabWidth: 110
 | 
			
		||||
        // minimumActiveTabWidth: 70,
 | 
			
		||||
        collapsible: true
 | 
			
		||||
        // scrollable: true
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var knownTabs = {
 | 
			
		||||
@@ -58,6 +60,8 @@ RED.sidebar = (function() {
 | 
			
		||||
            options = title;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete options.closeable;
 | 
			
		||||
 | 
			
		||||
        options.wrapper = $('<div>',{style:"height:100%"}).appendTo("#sidebar-content")
 | 
			
		||||
        options.wrapper.append(options.content);
 | 
			
		||||
        options.wrapper.hide();
 | 
			
		||||
@@ -81,6 +85,8 @@ RED.sidebar = (function() {
 | 
			
		||||
            group: "sidebar-tabs"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        options.iconClass = options.iconClass || "fa fa-square-o"
 | 
			
		||||
 | 
			
		||||
        knownTabs[options.id] = options;
 | 
			
		||||
 | 
			
		||||
        if (options.visible !== false) {
 | 
			
		||||
@@ -212,6 +218,7 @@ RED.sidebar = (function() {
 | 
			
		||||
        showSidebar();
 | 
			
		||||
        RED.sidebar.info.init();
 | 
			
		||||
        RED.sidebar.config.init();
 | 
			
		||||
        RED.sidebar.context.init();
 | 
			
		||||
        // hide info bar at start if screen rather narrow...
 | 
			
		||||
        if ($(window).width() < 600) { RED.menu.setSelected("menu-item-sidebar",false); }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,5 +23,6 @@ RED.state = {
 | 
			
		||||
    EXPORT: 6,
 | 
			
		||||
    IMPORT: 7,
 | 
			
		||||
    IMPORT_DRAGGING: 8,
 | 
			
		||||
    QUICK_JOINING: 9
 | 
			
		||||
    QUICK_JOINING: 9,
 | 
			
		||||
    PANNING: 10
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -139,7 +139,7 @@ RED.subflow = (function() {
 | 
			
		||||
        RED.view.select();
 | 
			
		||||
        RED.nodes.dirty(true);
 | 
			
		||||
        RED.view.redraw();
 | 
			
		||||
        $("#workspace-subflow-output .spinner-value").html(subflow.out.length);
 | 
			
		||||
        $("#workspace-subflow-output .spinner-value").text(subflow.out.length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeSubflowOutput(removedSubflowOutputs) {
 | 
			
		||||
@@ -216,7 +216,7 @@ RED.subflow = (function() {
 | 
			
		||||
            $("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
 | 
			
		||||
            $("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
 | 
			
		||||
 | 
			
		||||
            $("#workspace-subflow-output .spinner-value").html(activeSubflow.out.length);
 | 
			
		||||
            $("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -221,8 +221,7 @@ RED.sidebar.config = (function() {
 | 
			
		||||
            name: RED._("sidebar.config.name"),
 | 
			
		||||
            content: content,
 | 
			
		||||
            toolbar: toolbar,
 | 
			
		||||
            closeable: true,
 | 
			
		||||
            visible: false,
 | 
			
		||||
            iconClass: "fa fa-cog",
 | 
			
		||||
            onchange: function() { refreshConfigNodeList(); }
 | 
			
		||||
        });
 | 
			
		||||
        RED.actions.add("core:show-config-tab",function() {RED.sidebar.show('config')});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										292
									
								
								editor/js/ui/tab-context.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								editor/js/ui/tab-context.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,292 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
RED.sidebar.context = (function() {
 | 
			
		||||
 | 
			
		||||
    var content;
 | 
			
		||||
    var sections;
 | 
			
		||||
 | 
			
		||||
    var localCache = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var nodeSection;
 | 
			
		||||
    // var subflowSection;
 | 
			
		||||
    var flowSection;
 | 
			
		||||
    var globalSection;
 | 
			
		||||
 | 
			
		||||
    var currentNode;
 | 
			
		||||
    var currentFlow;
 | 
			
		||||
 | 
			
		||||
    function init() {
 | 
			
		||||
 | 
			
		||||
        content = $("<div>").css({"position":"relative","height":"100%"});
 | 
			
		||||
        content.className = "sidebar-context"
 | 
			
		||||
        // var toolbar = $('<div class="sidebar-header">'+
 | 
			
		||||
        //     '</div>').appendTo(content);
 | 
			
		||||
 | 
			
		||||
        var footerToolbar = $('<div>'+
 | 
			
		||||
            // '<span class="button-group"><a class="sidebar-footer-button" href="#" data-i18n="[title]node-red:debug.sidebar.openWindow"><i class="fa fa-desktop"></i></a></span> ' +
 | 
			
		||||
            '</div>');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var stackContainer = $("<div>",{class:"sidebar-context-stack"}).appendTo(content);
 | 
			
		||||
        sections = RED.stack.create({
 | 
			
		||||
            container: stackContainer
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        nodeSection = sections.add({
 | 
			
		||||
            title: RED._("sidebar.context.node"),
 | 
			
		||||
            collapsible: true,
 | 
			
		||||
            // onexpand: function() {
 | 
			
		||||
            //     updateNode(currentNode,true);
 | 
			
		||||
            // }
 | 
			
		||||
        });
 | 
			
		||||
        nodeSection.expand();
 | 
			
		||||
        nodeSection.content.css({height:"100%"});
 | 
			
		||||
        nodeSection.timestamp = $('<div class="sidebar-context-updated"> </div>').appendTo(nodeSection.content);
 | 
			
		||||
        var table = $('<table class="node-info"></table>').appendTo(nodeSection.content);
 | 
			
		||||
        nodeSection.table = $('<tbody>').appendTo(table);
 | 
			
		||||
        var bg = $('<div style="float: right"></div>').appendTo(nodeSection.header);
 | 
			
		||||
        $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>')
 | 
			
		||||
            .appendTo(bg)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                evt.stopPropagation();
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                updateNode(currentNode, true);
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        // subflowSection  = sections.add({
 | 
			
		||||
        //     title: "Subflow",
 | 
			
		||||
        //     collapsible: true
 | 
			
		||||
        // });
 | 
			
		||||
        // subflowSection.expand();
 | 
			
		||||
        // subflowSection.content.css({height:"100%"});
 | 
			
		||||
        // bg = $('<div style="float: right"></div>').appendTo(subflowSection.header);
 | 
			
		||||
        // $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>')
 | 
			
		||||
        //     .appendTo(bg)
 | 
			
		||||
        //     .click(function(evt) {
 | 
			
		||||
        //         evt.stopPropagation();
 | 
			
		||||
        //         evt.preventDefault();
 | 
			
		||||
        //     })
 | 
			
		||||
        //
 | 
			
		||||
        // subflowSection.container.hide();
 | 
			
		||||
 | 
			
		||||
        flowSection = sections.add({
 | 
			
		||||
            title: RED._("sidebar.context.flow"),
 | 
			
		||||
            collapsible: true
 | 
			
		||||
        });
 | 
			
		||||
        flowSection.expand();
 | 
			
		||||
        flowSection.content.css({height:"100%"});
 | 
			
		||||
        flowSection.timestamp = $('<div class="sidebar-context-updated"> </div>').appendTo(flowSection.content);
 | 
			
		||||
        var table = $('<table class="node-info"></table>').appendTo(flowSection.content);
 | 
			
		||||
        flowSection.table = $('<tbody>').appendTo(table);
 | 
			
		||||
        bg = $('<div style="float: right"></div>').appendTo(flowSection.header);
 | 
			
		||||
        $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>')
 | 
			
		||||
            .appendTo(bg)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                evt.stopPropagation();
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                updateFlow(currentFlow);
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        globalSection = sections.add({
 | 
			
		||||
            title: RED._("sidebar.context.global"),
 | 
			
		||||
            collapsible: true
 | 
			
		||||
        });
 | 
			
		||||
        globalSection.expand();
 | 
			
		||||
        globalSection.content.css({height:"100%"});
 | 
			
		||||
        globalSection.timestamp = $('<div class="sidebar-context-updated"> </div>').appendTo(globalSection.content);
 | 
			
		||||
        var table = $('<table class="node-info"></table>').appendTo(globalSection.content);
 | 
			
		||||
        globalSection.table = $('<tbody>').appendTo(table);
 | 
			
		||||
 | 
			
		||||
        bg = $('<div style="float: right"></div>').appendTo(globalSection.header);
 | 
			
		||||
        $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>')
 | 
			
		||||
            .appendTo(bg)
 | 
			
		||||
            .click(function(evt) {
 | 
			
		||||
                evt.stopPropagation();
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                updateEntry(globalSection,"context/global","global");
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:show-context-tab",show);
 | 
			
		||||
 | 
			
		||||
        RED.sidebar.addTab({
 | 
			
		||||
            id: "context",
 | 
			
		||||
            label: RED._("sidebar.context.label"),
 | 
			
		||||
            name: RED._("sidebar.context.name"),
 | 
			
		||||
            iconClass: "fa fa-database",
 | 
			
		||||
            content: content,
 | 
			
		||||
            toolbar: footerToolbar,
 | 
			
		||||
            // pinned: true,
 | 
			
		||||
            enableOnEdit: false
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // var toggleLiveButton = $("#sidebar-context-toggle-live");
 | 
			
		||||
        // toggleLiveButton.click(function(evt) {
 | 
			
		||||
        //     evt.preventDefault();
 | 
			
		||||
        //     if ($(this).hasClass("selected")) {
 | 
			
		||||
        //         $(this).removeClass("selected");
 | 
			
		||||
        //         $(this).find("i").removeClass("fa-pause");
 | 
			
		||||
        //         $(this).find("i").addClass("fa-play");
 | 
			
		||||
        //     } else {
 | 
			
		||||
        //         $(this).addClass("selected");
 | 
			
		||||
        //         $(this).find("i").removeClass("fa-play");
 | 
			
		||||
        //         $(this).find("i").addClass("fa-pause");
 | 
			
		||||
        //     }
 | 
			
		||||
        // });
 | 
			
		||||
        // RED.popover.tooltip(toggleLiveButton, function() {
 | 
			
		||||
        //     if (toggleLiveButton.hasClass("selected")) {
 | 
			
		||||
        //         return "Pause live updates"
 | 
			
		||||
        //     } else {
 | 
			
		||||
        //         return "Start live updates"
 | 
			
		||||
        //     }
 | 
			
		||||
        // });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        RED.events.on("view:selection-changed", function(event) {
 | 
			
		||||
            var selectedNode = event.nodes && event.nodes.length === 1 && event.nodes[0];
 | 
			
		||||
            updateNode(selectedNode);
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        RED.events.on("workspace:change", function(event) {
 | 
			
		||||
            updateFlow(RED.nodes.workspace(event.workspace));
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        updateEntry(globalSection,"context/global","global");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function updateNode(node,force) {
 | 
			
		||||
        currentNode = node;
 | 
			
		||||
        if (force) {
 | 
			
		||||
            if (node) {
 | 
			
		||||
                updateEntry(nodeSection,"context/node/"+node.id,node.id);
 | 
			
		||||
                // if (/^subflow:/.test(node.type)) {
 | 
			
		||||
                //     subflowSection.container.show();
 | 
			
		||||
                //     updateEntry(subflowSection,"context/flow/"+node.id,node.id);
 | 
			
		||||
                // } else {
 | 
			
		||||
                //     subflowSection.container.hide();
 | 
			
		||||
                // }
 | 
			
		||||
            } else {
 | 
			
		||||
                // subflowSection.container.hide();
 | 
			
		||||
                updateEntry(nodeSection)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $(nodeSection.table).empty();
 | 
			
		||||
            if (node) {
 | 
			
		||||
                $('<tr class="node-info-node-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.refresh"></td></tr>').appendTo(nodeSection.table).i18n();
 | 
			
		||||
            } else {
 | 
			
		||||
                $('<tr class="node-info-node-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.none"></td></tr>').appendTo(nodeSection.table).i18n();
 | 
			
		||||
            }
 | 
			
		||||
            nodeSection.timestamp.html(" ");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function updateFlow(flow) {
 | 
			
		||||
        currentFlow = flow;
 | 
			
		||||
        if (flow) {
 | 
			
		||||
            updateEntry(flowSection,"context/flow/"+flow.id,flow.id);
 | 
			
		||||
        } else {
 | 
			
		||||
            updateEntry(flowSection)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function refreshEntry(section,baseUrl,id) {
 | 
			
		||||
 | 
			
		||||
        var contextStores = RED.settings.context.stores;
 | 
			
		||||
        var container = section.table;
 | 
			
		||||
 | 
			
		||||
        $.getJSON(baseUrl, function(data) {
 | 
			
		||||
            $(container).empty();
 | 
			
		||||
            var sortedData = {};
 | 
			
		||||
            for (var store in data) {
 | 
			
		||||
                if (data.hasOwnProperty(store)) {
 | 
			
		||||
                    for (var key in data[store]) {
 | 
			
		||||
                        if (data[store].hasOwnProperty(key)) {
 | 
			
		||||
                            if (!sortedData.hasOwnProperty(key)) {
 | 
			
		||||
                                sortedData[key] = [];
 | 
			
		||||
                            }
 | 
			
		||||
                            data[store][key].store = store;
 | 
			
		||||
                            sortedData[key].push(data[store][key])
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var keys = Object.keys(sortedData);
 | 
			
		||||
            keys.sort();
 | 
			
		||||
            var l = keys.length;
 | 
			
		||||
            for (var i = 0; i < l; i++) {
 | 
			
		||||
                sortedData[keys[i]].forEach(function(v) {
 | 
			
		||||
                    var k = keys[i];
 | 
			
		||||
                    var l2 = sortedData[k].length;
 | 
			
		||||
                    var propRow = $('<tr class="node-info-node-row"><td class="sidebar-context-property"></td><td></td></tr>').appendTo(container);
 | 
			
		||||
                    var obj = $(propRow.children()[0]);
 | 
			
		||||
                    obj.text(k);
 | 
			
		||||
                    var tools = $('<span class="debug-message-tools button-group"></span>').appendTo(obj);
 | 
			
		||||
                    var refreshItem = $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).click(function(e) {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        e.stopPropagation();
 | 
			
		||||
                        $.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
 | 
			
		||||
                            $(propRow.children()[1]).empty();
 | 
			
		||||
                            var payload = data.msg;
 | 
			
		||||
                            var format = data.format;
 | 
			
		||||
                            payload = RED.utils.decodeObject(payload,format);
 | 
			
		||||
                            RED.utils.createObjectElement(payload, {
 | 
			
		||||
                                typeHint: data.format,
 | 
			
		||||
                                sourceId: id+"."+k
 | 
			
		||||
                            }).appendTo(propRow.children()[1]);
 | 
			
		||||
                        })
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    var payload = v.msg;
 | 
			
		||||
                    var format = v.format;
 | 
			
		||||
                    payload = RED.utils.decodeObject(payload,format);
 | 
			
		||||
                    RED.utils.createObjectElement(payload, {
 | 
			
		||||
                        typeHint: v.format,
 | 
			
		||||
                        sourceId: id+"."+k
 | 
			
		||||
                    }).appendTo(propRow.children()[1]);
 | 
			
		||||
                    if (contextStores.length > 1) {
 | 
			
		||||
                        $("<span>",{class:"sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            if (l === 0) {
 | 
			
		||||
                $('<tr class="node-info-node-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.empty"></td></tr>').appendTo(container).i18n();
 | 
			
		||||
            }
 | 
			
		||||
            $(section.timestamp).text(new Date().toLocaleString());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    function updateEntry(section,baseUrl,id) {
 | 
			
		||||
        var container = section.table;
 | 
			
		||||
        if (id) {
 | 
			
		||||
            refreshEntry(section,baseUrl,id);
 | 
			
		||||
        } else {
 | 
			
		||||
            $(container).empty();
 | 
			
		||||
            $('<tr class="node-info-node-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.none"></td></tr>').appendTo(container).i18n();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function show() {
 | 
			
		||||
        RED.sidebar.show("context");
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
        init: init
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
@@ -50,13 +50,15 @@ RED.sidebar.info = (function() {
 | 
			
		||||
        }).hide();
 | 
			
		||||
 | 
			
		||||
        nodeSection = sections.add({
 | 
			
		||||
            title: "Node",
 | 
			
		||||
            collapsible: false
 | 
			
		||||
            title: RED._("sidebar.info.info"),
 | 
			
		||||
            collapsible: true
 | 
			
		||||
        });
 | 
			
		||||
        nodeSection.expand();
 | 
			
		||||
        infoSection = sections.add({
 | 
			
		||||
            title: "Information",
 | 
			
		||||
            collapsible: false
 | 
			
		||||
            title: RED._("sidebar.info.nodeHelp"),
 | 
			
		||||
            collapsible: true
 | 
			
		||||
        });
 | 
			
		||||
        infoSection.expand();
 | 
			
		||||
        infoSection.content.css("padding","6px");
 | 
			
		||||
        infoSection.container.css("border-bottom","none");
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +83,9 @@ RED.sidebar.info = (function() {
 | 
			
		||||
            id: "info",
 | 
			
		||||
            label: RED._("sidebar.info.label"),
 | 
			
		||||
            name: RED._("sidebar.info.name"),
 | 
			
		||||
            iconClass: "fa fa-info",
 | 
			
		||||
            content: content,
 | 
			
		||||
            pinned: true,
 | 
			
		||||
            enableOnEdit: true
 | 
			
		||||
        });
 | 
			
		||||
        if (tips.enabled()) {
 | 
			
		||||
@@ -96,23 +100,7 @@ RED.sidebar.info = (function() {
 | 
			
		||||
        RED.sidebar.show("info");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function jsonFilter(key,value) {
 | 
			
		||||
        if (key === "") {
 | 
			
		||||
            return value;
 | 
			
		||||
        }
 | 
			
		||||
        var t = typeof value;
 | 
			
		||||
        if ($.isArray(value)) {
 | 
			
		||||
            return "[array:"+value.length+"]";
 | 
			
		||||
        } else if (t === "object") {
 | 
			
		||||
            return "[object]"
 | 
			
		||||
        } else if (t === "string") {
 | 
			
		||||
            if (value.length > 30) {
 | 
			
		||||
                return value.substring(0,30)+" ...";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: DRY - projects.js
 | 
			
		||||
    function addTargetToExternalLinks(el) {
 | 
			
		||||
        $(el).find("a").each(function(el) {
 | 
			
		||||
            var href = $(this).attr('href');
 | 
			
		||||
@@ -123,76 +111,42 @@ RED.sidebar.info = (function() {
 | 
			
		||||
        return el;
 | 
			
		||||
    }
 | 
			
		||||
    function refresh(node) {
 | 
			
		||||
        if (node === undefined) {
 | 
			
		||||
            refreshSelection();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        sections.show();
 | 
			
		||||
        $(nodeSection.content).empty();
 | 
			
		||||
        $(infoSection.content).empty();
 | 
			
		||||
 | 
			
		||||
        var table = $('<table class="node-info"></table>');
 | 
			
		||||
        var tableBody = $('<tbody>').appendTo(table);
 | 
			
		||||
        var propRow;
 | 
			
		||||
 | 
			
		||||
        var table = $('<table class="node-info"></table>').appendTo(nodeSection.content);
 | 
			
		||||
        var tableBody = $('<tbody>').appendTo(table);
 | 
			
		||||
        var subflowNode;
 | 
			
		||||
        if (node.type === "tab") {
 | 
			
		||||
            nodeSection.title.html("Flow");
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>Name</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
            $(propRow.children()[1]).html(' '+(node.label||""))
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.id")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
            RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>Status</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
            $(propRow.children()[1]).html((!!!node.disabled)?"Enabled":"Disabled")
 | 
			
		||||
        var subflowUserCount;
 | 
			
		||||
 | 
			
		||||
        var activeProject = RED.projects.getActiveProject();
 | 
			
		||||
        if (activeProject) {
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>Project</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
            $(propRow.children()[1]).text(activeProject.name||"");
 | 
			
		||||
            $('<tr class="node-info-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody);
 | 
			
		||||
            $('<button class="editor-button editor-button-small" style="position:absolute;right:2px;"><i class="fa fa-ellipsis-h"></i></button>')
 | 
			
		||||
                .appendTo(propRow.children()[1])
 | 
			
		||||
                .click(function(evt) {
 | 
			
		||||
                    evt.preventDefault();
 | 
			
		||||
                    RED.projects.editProject();
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
        infoSection.container.show();
 | 
			
		||||
        if (node === null) {
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (Array.isArray(node)) {
 | 
			
		||||
            infoSection.container.hide();
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.selection")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
            $(propRow.children()[1]).text(RED._("sidebar.info.nodes",{count:node.length}))
 | 
			
		||||
        } else {
 | 
			
		||||
            nodeSection.title.html("Node");
 | 
			
		||||
            if (node.type !== "subflow" && node.name) {
 | 
			
		||||
                $('<tr class="node-info-node-row"><td>'+RED._("common.label.name")+'</td><td> <span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>').appendTo(tableBody);
 | 
			
		||||
            }
 | 
			
		||||
            $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>").appendTo(tableBody);
 | 
			
		||||
            propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.id")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
            RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
 | 
			
		||||
 | 
			
		||||
            var m = /^subflow(:(.+))?$/.exec(node.type);
 | 
			
		||||
 | 
			
		||||
            if (!m && node.type != "subflow" && node.type != "comment") {
 | 
			
		||||
                if (node._def) {
 | 
			
		||||
                    var count = 0;
 | 
			
		||||
                    var defaults = node._def.defaults;
 | 
			
		||||
                    for (var n in defaults) {
 | 
			
		||||
                        if (n != "name" && defaults.hasOwnProperty(n)) {
 | 
			
		||||
                            var val = node[n];
 | 
			
		||||
                            var type = typeof val;
 | 
			
		||||
                            count++;
 | 
			
		||||
                            propRow = $('<tr class="node-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
                            if (defaults[n].type) {
 | 
			
		||||
                                var configNode = RED.nodes.node(val);
 | 
			
		||||
                                if (!configNode) {
 | 
			
		||||
                                    RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    var configLabel = RED.utils.getNodeLabel(configNode,val);
 | 
			
		||||
                                    var container = propRow.children()[1];
 | 
			
		||||
 | 
			
		||||
                                    var div = $('<span>',{class:""}).appendTo(container);
 | 
			
		||||
                                    var nodeDiv = $('<div>',{class:"palette_node palette_node_small"}).appendTo(div);
 | 
			
		||||
                                    var colour = configNode._def.color;
 | 
			
		||||
                                    var icon_url = RED.utils.getNodeIcon(configNode._def);
 | 
			
		||||
                                    nodeDiv.css({'backgroundColor':colour, "cursor":"pointer"});
 | 
			
		||||
                                    var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
 | 
			
		||||
                                    $('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
 | 
			
		||||
                                    var nodeContainer = $('<span></span>').css({"verticalAlign":"top","marginLeft":"6px"}).html(configLabel).appendTo(container);
 | 
			
		||||
 | 
			
		||||
                                    nodeDiv.on('dblclick',function() {
 | 
			
		||||
                                        RED.editor.editConfig("", configNode.type, configNode.id);
 | 
			
		||||
                                    })
 | 
			
		||||
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                RED.utils.createObjectElement(val).appendTo(propRow.children()[1]);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (count > 0) {
 | 
			
		||||
                        $('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">show more</span><span class="node-info-property-show-less">show less</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (m) {
 | 
			
		||||
                if (m[2]) {
 | 
			
		||||
                    subflowNode = RED.nodes.subflow(m[2]);
 | 
			
		||||
@@ -200,49 +154,140 @@ RED.sidebar.info = (function() {
 | 
			
		||||
                    subflowNode = node;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody);
 | 
			
		||||
 | 
			
		||||
                var userCount = 0;
 | 
			
		||||
                subflowUserCount = 0;
 | 
			
		||||
                var subflowType = "subflow:"+subflowNode.id;
 | 
			
		||||
                RED.nodes.eachNode(function(n) {
 | 
			
		||||
                    if (n.type === subflowType) {
 | 
			
		||||
                        userCount++;
 | 
			
		||||
                        subflowUserCount++;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                $('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>').appendTo(tableBody);
 | 
			
		||||
                $('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+userCount+'</td></tr>').appendTo(tableBody);
 | 
			
		||||
            }
 | 
			
		||||
            if (node.type === "tab" || node.type === "subflow") {
 | 
			
		||||
                propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info."+(node.type==='tab'?'flow':'subflow'))+'</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
                RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
 | 
			
		||||
                propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.tabName")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
                $(propRow.children()[1]).text(node.label||node.name||"");
 | 
			
		||||
                if (node.type === "tab") {
 | 
			
		||||
                    propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
                    $(propRow.children()[1]).text((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled"))
 | 
			
		||||
                } else if (node.type === "subflow") {
 | 
			
		||||
                    propRow = $('<tr class="node-info-node-row"><td>'+RED._("subflow.category")+'</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
                    var category = node.category||"subflows";
 | 
			
		||||
                    $(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category}))
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.node")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
                RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (node.type !== "subflow" && node.type !== "unknown" && node.name) {
 | 
			
		||||
                    propRow = $('<tr class="node-info-node-row"><td>'+RED._("common.label.name")+'</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
                    $('<span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'"></span>').text(node.name).appendTo(propRow.children()[1]);
 | 
			
		||||
                }
 | 
			
		||||
                if (!m) {
 | 
			
		||||
                    propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.type")+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
                    $(propRow.children()[1]).text((node.type === "unknown")?node._orig.type:node.type);
 | 
			
		||||
                    if (node.type === "unknown") {
 | 
			
		||||
                        $('<span style="float: right; font-size: 0.8em"><i class="fa fa-warning"></i></span>').prependTo($(propRow.children()[1]))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (!m && node.type != "subflow" && node.type != "comment") {
 | 
			
		||||
                    var defaults;
 | 
			
		||||
                    if (node.type === 'unknown') {
 | 
			
		||||
                        defaults = {};
 | 
			
		||||
                        Object.keys(node._orig).forEach(function(k) {
 | 
			
		||||
                            if (k !== 'type') {
 | 
			
		||||
                                defaults[k] = {};
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                    } else if (node._def) {
 | 
			
		||||
                        defaults = node._def.defaults;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (defaults) {
 | 
			
		||||
                        var count = 0;
 | 
			
		||||
                        for (var n in defaults) {
 | 
			
		||||
                            if (n != "name" && defaults.hasOwnProperty(n)) {
 | 
			
		||||
                                var val = node[n];
 | 
			
		||||
                                var type = typeof val;
 | 
			
		||||
                                count++;
 | 
			
		||||
                                propRow = $('<tr class="node-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody);
 | 
			
		||||
                                if (defaults[n].type) {
 | 
			
		||||
                                    var configNode = RED.nodes.node(val);
 | 
			
		||||
                                    if (!configNode) {
 | 
			
		||||
                                        RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        var configLabel = RED.utils.getNodeLabel(configNode,val);
 | 
			
		||||
                                        var container = propRow.children()[1];
 | 
			
		||||
 | 
			
		||||
                                        var div = $('<span>',{class:""}).appendTo(container);
 | 
			
		||||
                                        var nodeDiv = $('<div>',{class:"palette_node palette_node_small"}).appendTo(div);
 | 
			
		||||
                                        var colour = RED.utils.getNodeColor(configNode.type,configNode._def);
 | 
			
		||||
                                        var icon_url = RED.utils.getNodeIcon(configNode._def);
 | 
			
		||||
                                        nodeDiv.css({'backgroundColor':colour, "cursor":"pointer"});
 | 
			
		||||
                                        var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
 | 
			
		||||
                                        $('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
 | 
			
		||||
                                        var nodeContainer = $('<span></span>').css({"verticalAlign":"top","marginLeft":"6px"}).text(configLabel).appendTo(container);
 | 
			
		||||
 | 
			
		||||
                                        nodeDiv.on('dblclick',function() {
 | 
			
		||||
                                            RED.editor.editConfig("", configNode.type, configNode.id);
 | 
			
		||||
                                        })
 | 
			
		||||
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    RED.utils.createObjectElement(val).appendTo(propRow.children()[1]);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (count > 0) {
 | 
			
		||||
                            $('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">'+RED._("sidebar.info.showMore")+'</span><span class="node-info-property-show-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (node.type !== 'tab') {
 | 
			
		||||
                    if (m) {
 | 
			
		||||
                        $('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody);
 | 
			
		||||
                        $('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>').appendTo(tableBody);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (m) {
 | 
			
		||||
                propRow = $('<tr class="node-info-node-row"><td>'+RED._("subflow.category")+'</td><td></td></tr>').appendTo(tableBody);
 | 
			
		||||
                var category = subflowNode.category||"subflows";
 | 
			
		||||
                $(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category}))
 | 
			
		||||
                $('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var infoText = "";
 | 
			
		||||
            if (!subflowNode && node.type !== "comment" && node.type !== "tab") {
 | 
			
		||||
                infoSection.title.text(RED._("sidebar.info.nodeHelp"));
 | 
			
		||||
                var helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
 | 
			
		||||
                infoText = helpText;
 | 
			
		||||
            } else if (node.type === "tab") {
 | 
			
		||||
                infoSection.title.text(RED._("sidebar.info.flowDesc"));
 | 
			
		||||
                infoText = marked(node.info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (subflowNode) {
 | 
			
		||||
                infoText = infoText + (marked(subflowNode.info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>'));
 | 
			
		||||
                infoSection.title.text(RED._("sidebar.info.subflowDesc"));
 | 
			
		||||
            } else if (node._def && node._def.info) {
 | 
			
		||||
                infoSection.title.text(RED._("sidebar.info.nodeHelp"));
 | 
			
		||||
                var info = node._def.info;
 | 
			
		||||
                var textInfo = (typeof info === "function" ? info.call(node) : info);
 | 
			
		||||
                // TODO: help
 | 
			
		||||
                infoText = infoText + marked(textInfo);
 | 
			
		||||
            }
 | 
			
		||||
            if (infoText) {
 | 
			
		||||
                setInfoText(infoText);
 | 
			
		||||
            }
 | 
			
		||||
            $(".sidebar-node-info-stack").scrollTop(0);
 | 
			
		||||
            $(".node-info-property-header").click(function(e) {
 | 
			
		||||
                e.preventDefault();
 | 
			
		||||
                expandedSections["property"] = !expandedSections["property"];
 | 
			
		||||
                $(this).toggleClass("expanded",expandedSections["property"]);
 | 
			
		||||
                $(".node-info-property-row").toggle(expandedSections["property"]);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        $(table).appendTo(nodeSection.content);
 | 
			
		||||
 | 
			
		||||
        var infoText = "";
 | 
			
		||||
 | 
			
		||||
        if (!subflowNode && node.type !== "comment" && node.type !== "tab") {
 | 
			
		||||
            var helpText = $("script[data-help-name='"+node.type+"']").html()||"";
 | 
			
		||||
            infoText = helpText;
 | 
			
		||||
        } else if (node.type === "tab") {
 | 
			
		||||
            infoText = marked(node.info||"");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (subflowNode) {
 | 
			
		||||
            infoText = infoText + marked(subflowNode.info||"");
 | 
			
		||||
        } else if (node._def && node._def.info) {
 | 
			
		||||
            var info = node._def.info;
 | 
			
		||||
            var textInfo = (typeof info === "function" ? info.call(node) : info);
 | 
			
		||||
            // TODO: help
 | 
			
		||||
            infoText = infoText + marked(textInfo);
 | 
			
		||||
        }
 | 
			
		||||
        if (infoText) {
 | 
			
		||||
            setInfoText(infoText);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $(".node-info-property-header").click(function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            expandedSections["property"] = !expandedSections["property"];
 | 
			
		||||
            $(this).toggleClass("expanded",expandedSections["property"]);
 | 
			
		||||
            $(".node-info-property-row").toggle(expandedSections["property"]);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    function setInfoText(infoText) {
 | 
			
		||||
        var info = addTargetToExternalLinks($('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(infoSection.content);
 | 
			
		||||
@@ -342,22 +387,24 @@ RED.sidebar.info = (function() {
 | 
			
		||||
    })();
 | 
			
		||||
 | 
			
		||||
    function clear() {
 | 
			
		||||
        sections.hide();
 | 
			
		||||
        //
 | 
			
		||||
        // sections.hide();
 | 
			
		||||
        refresh(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function set(html) {
 | 
			
		||||
    function set(html,title) {
 | 
			
		||||
        // tips.stop();
 | 
			
		||||
        sections.show();
 | 
			
		||||
        // sections.show();
 | 
			
		||||
        refresh(null);
 | 
			
		||||
        nodeSection.container.hide();
 | 
			
		||||
        $(infoSection.content).empty();
 | 
			
		||||
        infoSection.title.text(title||RED._("sidebar.info.info"));
 | 
			
		||||
        setInfoText(html);
 | 
			
		||||
        $(".sidebar-node-info-stack").scrollTop(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    RED.events.on("view:selection-changed",function(selection) {
 | 
			
		||||
    function refreshSelection(selection) {
 | 
			
		||||
        if (selection === undefined) {
 | 
			
		||||
            selection = RED.view.selection();
 | 
			
		||||
        }
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            if (selection.nodes.length == 1) {
 | 
			
		||||
                var node = selection.nodes[0];
 | 
			
		||||
@@ -366,6 +413,8 @@ RED.sidebar.info = (function() {
 | 
			
		||||
                } else {
 | 
			
		||||
                    refresh(node);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                refresh(selection.nodes);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            var activeWS = RED.workspaces.active();
 | 
			
		||||
@@ -378,11 +427,15 @@ RED.sidebar.info = (function() {
 | 
			
		||||
                if (workspace && workspace.info) {
 | 
			
		||||
                    refresh(workspace);
 | 
			
		||||
                } else {
 | 
			
		||||
                    clear();
 | 
			
		||||
                    refresh(null)
 | 
			
		||||
                    // clear();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    RED.events.on("view:selection-changed",refreshSelection);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: init,
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,15 @@ RED.tray = (function() {
 | 
			
		||||
        // var growButton = $('<a class="editor-tray-resize-button" style="cursor: w-resize;"><i class="fa fa-angle-left"></i></a>').appendTo(resizer);
 | 
			
		||||
        // var shrinkButton = $('<a class="editor-tray-resize-button" style="cursor: e-resize;"><i style="margin-left: 1px;" class="fa fa-angle-right"></i></a>').appendTo(resizer);
 | 
			
		||||
        if (options.title) {
 | 
			
		||||
            $('<div class="editor-tray-titlebar">'+options.title+'</div>').appendTo(header);
 | 
			
		||||
            var titles = stack.map(function(e) { return e.options.title });
 | 
			
		||||
            titles.push(options.title);
 | 
			
		||||
            var title = '<ul class="editor-tray-breadcrumbs"><li>'+titles.join("</li><li>")+'</li></ul>';
 | 
			
		||||
 | 
			
		||||
            $('<div class="editor-tray-titlebar">'+title+'</div>').appendTo(header);
 | 
			
		||||
        }
 | 
			
		||||
        if (options.width === Infinity) {
 | 
			
		||||
            options.maximized = true;
 | 
			
		||||
            resizer.addClass('editor-tray-resize-maximised');
 | 
			
		||||
        }
 | 
			
		||||
        var buttonBar = $('<div class="editor-tray-toolbar"></div>').appendTo(header);
 | 
			
		||||
        var primaryButton;
 | 
			
		||||
@@ -44,7 +52,7 @@ RED.tray = (function() {
 | 
			
		||||
                    b.attr('id',button.id);
 | 
			
		||||
                }
 | 
			
		||||
                if (button.text) {
 | 
			
		||||
                    b.html(button.text);
 | 
			
		||||
                    b.text(button.text);
 | 
			
		||||
                }
 | 
			
		||||
                if (button.click) {
 | 
			
		||||
                    b.click((function(action) {
 | 
			
		||||
@@ -74,7 +82,8 @@ RED.tray = (function() {
 | 
			
		||||
        };
 | 
			
		||||
        stack.push(tray);
 | 
			
		||||
 | 
			
		||||
        el.draggable({
 | 
			
		||||
        if (!options.maximized) {
 | 
			
		||||
            el.draggable({
 | 
			
		||||
                handle: resizer,
 | 
			
		||||
                axis: "x",
 | 
			
		||||
                start:function(event,ui) {
 | 
			
		||||
@@ -103,16 +112,17 @@ RED.tray = (function() {
 | 
			
		||||
                    tray.width = -ui.position.left;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function finishBuild() {
 | 
			
		||||
            $("#header-shade").show();
 | 
			
		||||
            $("#editor-shade").show();
 | 
			
		||||
            $("#palette-shade").show();
 | 
			
		||||
            $(".sidebar-shade").show();
 | 
			
		||||
 | 
			
		||||
            tray.preferredWidth = Math.max(el.width(),500);
 | 
			
		||||
            body.css({"minWidth":tray.preferredWidth-40});
 | 
			
		||||
 | 
			
		||||
            if (!options.maximized) {
 | 
			
		||||
                body.css({"minWidth":tray.preferredWidth-40});
 | 
			
		||||
            }
 | 
			
		||||
            if (options.width) {
 | 
			
		||||
                if (options.width > $("#editor-stack").position().left-8) {
 | 
			
		||||
                    options.width = $("#editor-stack").position().left-8;
 | 
			
		||||
@@ -175,7 +185,7 @@ RED.tray = (function() {
 | 
			
		||||
            var tray = stack[stack.length-1];
 | 
			
		||||
            var trayHeight = tray.tray.height()-tray.header.outerHeight()-tray.footer.outerHeight();
 | 
			
		||||
            tray.body.height(trayHeight);
 | 
			
		||||
            if (tray.width > $("#editor-stack").position().left-8) {
 | 
			
		||||
            if (tray.options.maximized || tray.width > $("#editor-stack").position().left-8) {
 | 
			
		||||
                tray.width = $("#editor-stack").position().left-8;
 | 
			
		||||
                tray.tray.width(tray.width);
 | 
			
		||||
                // tray.body.parent().width(tray.width);
 | 
			
		||||
@@ -204,8 +214,11 @@ RED.tray = (function() {
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        show: function show(options) {
 | 
			
		||||
            if (stack.length > 0) {
 | 
			
		||||
            if (stack.length > 0 && !options.overlay) {
 | 
			
		||||
                var oldTray = stack[stack.length-1];
 | 
			
		||||
                if (options.width === "inherit") {
 | 
			
		||||
                    options.width = oldTray.tray.width();
 | 
			
		||||
                }
 | 
			
		||||
                oldTray.tray.css({
 | 
			
		||||
                    right: -(oldTray.tray.width()+10)+"px"
 | 
			
		||||
                });
 | 
			
		||||
@@ -232,14 +245,21 @@ RED.tray = (function() {
 | 
			
		||||
                    tray.tray.remove();
 | 
			
		||||
                    if (stack.length > 0) {
 | 
			
		||||
                        var oldTray = stack[stack.length-1];
 | 
			
		||||
                        oldTray.tray.appendTo("#editor-stack");
 | 
			
		||||
                        setTimeout(function() {
 | 
			
		||||
                        if (!oldTray.options.overlay) {
 | 
			
		||||
                            oldTray.tray.appendTo("#editor-stack");
 | 
			
		||||
                            setTimeout(function() {
 | 
			
		||||
                                handleWindowResize();
 | 
			
		||||
                                oldTray.tray.css({right:0});
 | 
			
		||||
                                if (oldTray.options.show) {
 | 
			
		||||
                                    oldTray.options.show();
 | 
			
		||||
                                }
 | 
			
		||||
                            },0);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            handleWindowResize();
 | 
			
		||||
                            oldTray.tray.css({right:0});
 | 
			
		||||
                            if (oldTray.options.show) {
 | 
			
		||||
                                oldTray.options.show();
 | 
			
		||||
                            }
 | 
			
		||||
                        },0);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (done) {
 | 
			
		||||
                        done();
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,14 @@ RED.typeSearch = (function() {
 | 
			
		||||
 | 
			
		||||
    var activeFilter = "";
 | 
			
		||||
    var addCallback;
 | 
			
		||||
    var cancelCallback;
 | 
			
		||||
 | 
			
		||||
    var typesUsed = {};
 | 
			
		||||
 | 
			
		||||
    function search(val) {
 | 
			
		||||
        activeFilter = val.toLowerCase();
 | 
			
		||||
        var visible = searchResults.editableList('filter');
 | 
			
		||||
        searchResults.editableList('sort');
 | 
			
		||||
        setTimeout(function() {
 | 
			
		||||
            selected = 0;
 | 
			
		||||
            searchResults.children().removeClass('selected');
 | 
			
		||||
@@ -100,6 +102,23 @@ RED.typeSearch = (function() {
 | 
			
		||||
                }
 | 
			
		||||
                return (activeFilter==="")||(data.index.indexOf(activeFilter) > -1);
 | 
			
		||||
            },
 | 
			
		||||
            sort: function(A,B) {
 | 
			
		||||
                if (activeFilter === "") {
 | 
			
		||||
                    return A.i - B.i;
 | 
			
		||||
                }
 | 
			
		||||
                var Ai = A.index.indexOf(activeFilter);
 | 
			
		||||
                var Bi = B.index.indexOf(activeFilter);
 | 
			
		||||
                if (Ai === -1) {
 | 
			
		||||
                    return 1;
 | 
			
		||||
                }
 | 
			
		||||
                if (Bi === -1) {
 | 
			
		||||
                    return -1;
 | 
			
		||||
                }
 | 
			
		||||
                if (Ai === Bi) {
 | 
			
		||||
                    return sortTypeLabels(A,B);
 | 
			
		||||
                }
 | 
			
		||||
                return Ai-Bi;
 | 
			
		||||
            },
 | 
			
		||||
            addItem: function(container,i,object) {
 | 
			
		||||
                var def = object.def;
 | 
			
		||||
                object.index = object.type.toLowerCase();
 | 
			
		||||
@@ -109,7 +128,7 @@ RED.typeSearch = (function() {
 | 
			
		||||
                var div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
 | 
			
		||||
 | 
			
		||||
                var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
 | 
			
		||||
                var colour = def.color;
 | 
			
		||||
                var colour = RED.utils.getNodeColor(object.type,def);
 | 
			
		||||
                var icon_url = RED.utils.getNodeIcon(def);
 | 
			
		||||
                nodeDiv.css('backgroundColor',colour);
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +147,7 @@ RED.typeSearch = (function() {
 | 
			
		||||
                var label = object.label;
 | 
			
		||||
                object.index += "|"+label.toLowerCase();
 | 
			
		||||
 | 
			
		||||
                $('<div>',{class:"red-ui-search-result-node-label"}).html(label).appendTo(contentDiv);
 | 
			
		||||
                $('<div>',{class:"red-ui-search-result-node-label"}).text(label).appendTo(contentDiv);
 | 
			
		||||
 | 
			
		||||
                div.click(function(evt) {
 | 
			
		||||
                    evt.preventDefault();
 | 
			
		||||
@@ -155,11 +174,19 @@ RED.typeSearch = (function() {
 | 
			
		||||
                t = t.parent();
 | 
			
		||||
            }
 | 
			
		||||
            hide(true);
 | 
			
		||||
            if (cancelCallback) {
 | 
			
		||||
                cancelCallback();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function show(opts) {
 | 
			
		||||
        if (!visible) {
 | 
			
		||||
            RED.keyboard.add("*","escape",function(){hide()});
 | 
			
		||||
            RED.keyboard.add("*","escape",function(){
 | 
			
		||||
                hide();
 | 
			
		||||
                if (cancelCallback) {
 | 
			
		||||
                    cancelCallback();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (dialog === null) {
 | 
			
		||||
                createDialog();
 | 
			
		||||
            }
 | 
			
		||||
@@ -175,6 +202,7 @@ RED.typeSearch = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        refreshTypeList();
 | 
			
		||||
        addCallback = opts.add;
 | 
			
		||||
        closeCallback = opts.close;
 | 
			
		||||
        RED.events.emit("type-search:open");
 | 
			
		||||
        //shade.show();
 | 
			
		||||
        dialog.css({left:opts.x+"px",top:opts.y+"px"}).show();
 | 
			
		||||
@@ -215,7 +243,17 @@ RED.typeSearch = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        return label;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sortTypeLabels(a,b) {
 | 
			
		||||
        var al = a.label.toLowerCase();
 | 
			
		||||
        var bl = b.label.toLowerCase();
 | 
			
		||||
        if (al < bl) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        } else if (al === bl) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function refreshTypeList() {
 | 
			
		||||
        var i;
 | 
			
		||||
        searchResults.editableList('empty');
 | 
			
		||||
@@ -236,41 +274,37 @@ RED.typeSearch = (function() {
 | 
			
		||||
        var items = [];
 | 
			
		||||
        RED.nodes.registry.getNodeTypes().forEach(function(t) {
 | 
			
		||||
            var def = RED.nodes.getType(t);
 | 
			
		||||
            if (def.category !== 'config' && t !== 'unknown') {
 | 
			
		||||
            if (def.category !== 'config' && t !== 'unknown' && t !== 'tab') {
 | 
			
		||||
                items.push({type:t,def: def, label:getTypeLabel(t,def)});
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        items.sort(function(a,b) {
 | 
			
		||||
            var al = a.label.toLowerCase();
 | 
			
		||||
            var bl = b.label.toLowerCase();
 | 
			
		||||
            if (al < bl) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            } else if (al === bl) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        items.sort(sortTypeLabels);
 | 
			
		||||
 | 
			
		||||
        var commonCount = 0;
 | 
			
		||||
        var item;
 | 
			
		||||
        var index = 0;
 | 
			
		||||
        for(i=0;i<common.length;i++) {
 | 
			
		||||
            item = {
 | 
			
		||||
                type: common[i],
 | 
			
		||||
                common: true,
 | 
			
		||||
                def: RED.nodes.getType(common[i])
 | 
			
		||||
            };
 | 
			
		||||
            item.label = getTypeLabel(item.type,item.def);
 | 
			
		||||
            if (i === common.length-1) {
 | 
			
		||||
                item.separator = true;
 | 
			
		||||
            var itemDef = RED.nodes.getType(common[i]);
 | 
			
		||||
            if (itemDef) {
 | 
			
		||||
                item = {
 | 
			
		||||
                    type: common[i],
 | 
			
		||||
                    common: true,
 | 
			
		||||
                    def: itemDef,
 | 
			
		||||
                    i: index++
 | 
			
		||||
                };
 | 
			
		||||
                item.label = getTypeLabel(item.type,item.def);
 | 
			
		||||
                if (i === common.length-1) {
 | 
			
		||||
                    item.separator = true;
 | 
			
		||||
                }
 | 
			
		||||
                searchResults.editableList('addItem', item);
 | 
			
		||||
            }
 | 
			
		||||
            searchResults.editableList('addItem', item);
 | 
			
		||||
        }
 | 
			
		||||
        for(i=0;i<Math.min(5,recentlyUsed.length);i++) {
 | 
			
		||||
            item = {
 | 
			
		||||
                type:recentlyUsed[i],
 | 
			
		||||
                def: RED.nodes.getType(recentlyUsed[i]),
 | 
			
		||||
                recent: true
 | 
			
		||||
                recent: true,
 | 
			
		||||
                i: index++
 | 
			
		||||
            };
 | 
			
		||||
            item.label = getTypeLabel(item.type,item.def);
 | 
			
		||||
            if (i === recentlyUsed.length-1) {
 | 
			
		||||
@@ -279,6 +313,7 @@ RED.typeSearch = (function() {
 | 
			
		||||
            searchResults.editableList('addItem', item);
 | 
			
		||||
        }
 | 
			
		||||
        for (i=0;i<items.length;i++) {
 | 
			
		||||
            items[i].i = index++;
 | 
			
		||||
            searchResults.editableList('addItem', items[i]);
 | 
			
		||||
        }
 | 
			
		||||
        setTimeout(function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,15 @@ RED.userSettings = (function() {
 | 
			
		||||
        if (settingsVisible) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!RED.user.hasPermission("settings.write")) {
 | 
			
		||||
            RED.notify(RED._("user.errors.settings"),"error");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        settingsVisible = true;
 | 
			
		||||
        var tabContainer;
 | 
			
		||||
 | 
			
		||||
        var trayOptions = {
 | 
			
		||||
            title: "User Settings",
 | 
			
		||||
            title: RED._("menu.label.userSettings"),
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    id: "node-dialog-ok",
 | 
			
		||||
@@ -100,7 +104,7 @@ RED.userSettings = (function() {
 | 
			
		||||
 | 
			
		||||
    var viewSettings = [
 | 
			
		||||
        {
 | 
			
		||||
            title: "Grid",
 | 
			
		||||
            title: "menu.label.view.grid",
 | 
			
		||||
            options: [
 | 
			
		||||
                {setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid",toggle:true,onchange:"core:toggle-show-grid"},
 | 
			
		||||
                {setting:"view-snap-grid",oldSetting:"menu-menu-item-view-snap-grid",label:"menu.label.view.snapGrid",toggle:true,onchange:"core:toggle-snap-grid"},
 | 
			
		||||
@@ -108,13 +112,13 @@ RED.userSettings = (function() {
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title: "Nodes",
 | 
			
		||||
            title: "menu.label.nodes",
 | 
			
		||||
            options: [
 | 
			
		||||
                {setting:"view-node-status",oldSetting:"menu-menu-item-status",label:"menu.label.displayStatus",default: true, toggle:true,onchange:"core:toggle-status"}
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title: "Other",
 | 
			
		||||
            title: "menu.label.other",
 | 
			
		||||
            options: [
 | 
			
		||||
                {setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"}
 | 
			
		||||
            ]
 | 
			
		||||
@@ -127,10 +131,13 @@ RED.userSettings = (function() {
 | 
			
		||||
 | 
			
		||||
        var pane = $('<div id="user-settings-tab-view" class="node-help"></div>');
 | 
			
		||||
 | 
			
		||||
        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
        currentEditorSettings.view = currentEditorSettings.view || {};
 | 
			
		||||
 | 
			
		||||
        viewSettings.forEach(function(section) {
 | 
			
		||||
            $('<h3></h3>').text(section.title).appendTo(pane);
 | 
			
		||||
            $('<h3></h3>').text(RED._(section.title)).appendTo(pane);
 | 
			
		||||
            section.options.forEach(function(opt) {
 | 
			
		||||
                var initialState = RED.settings.get(opt.setting);
 | 
			
		||||
                var initialState = currentEditorSettings.view[opt.setting];
 | 
			
		||||
                var row = $('<div class="user-settings-row"></div>').appendTo(pane);
 | 
			
		||||
                var input;
 | 
			
		||||
                if (opt.toggle) {
 | 
			
		||||
@@ -147,7 +154,10 @@ RED.userSettings = (function() {
 | 
			
		||||
 | 
			
		||||
    function setSelected(id, value) {
 | 
			
		||||
        var opt = allSettings[id];
 | 
			
		||||
        RED.settings.set(opt.setting,value);
 | 
			
		||||
        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
        currentEditorSettings.view = currentEditorSettings.view || {};
 | 
			
		||||
        currentEditorSettings.view[opt.setting] = value;
 | 
			
		||||
        RED.settings.set('editor', currentEditorSettings);
 | 
			
		||||
        var callback = opt.onchange;
 | 
			
		||||
        if (typeof callback === 'string') {
 | 
			
		||||
            callback = RED.actions.get(callback);
 | 
			
		||||
@@ -158,8 +168,9 @@ RED.userSettings = (function() {
 | 
			
		||||
    }
 | 
			
		||||
    function toggle(id) {
 | 
			
		||||
        var opt = allSettings[id];
 | 
			
		||||
        var state = RED.settings.get(opt.setting);
 | 
			
		||||
        setSelected(id,!state);
 | 
			
		||||
        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
        currentEditorSettings.view = currentEditorSettings.view || {};
 | 
			
		||||
        setSelected(id,!currentEditorSettings.view[opt.setting]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -169,7 +180,7 @@ RED.userSettings = (function() {
 | 
			
		||||
 | 
			
		||||
        addPane({
 | 
			
		||||
            id:'view',
 | 
			
		||||
            title: 'View',
 | 
			
		||||
            title: RED._("menu.label.view.view"),
 | 
			
		||||
            get: createViewPane,
 | 
			
		||||
            close: function() {
 | 
			
		||||
                viewSettings.forEach(function(section) {
 | 
			
		||||
@@ -185,21 +196,26 @@ RED.userSettings = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        var currentEditorSettings = RED.settings.get('editor') || {};
 | 
			
		||||
        currentEditorSettings.view = currentEditorSettings.view || {};
 | 
			
		||||
        var editorSettingsChanged = false;
 | 
			
		||||
        viewSettings.forEach(function(section) {
 | 
			
		||||
            section.options.forEach(function(opt) {
 | 
			
		||||
                if (opt.oldSetting) {
 | 
			
		||||
                    var oldValue = RED.settings.get(opt.oldSetting);
 | 
			
		||||
                    if (oldValue !== undefined && oldValue !== null) {
 | 
			
		||||
                        RED.settings.set(opt.setting,oldValue);
 | 
			
		||||
                        currentEditorSettings.view[opt.setting] = oldValue;
 | 
			
		||||
                        editorSettingsChanged = true;
 | 
			
		||||
                        RED.settings.remove(opt.oldSetting);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                allSettings[opt.setting] = opt;
 | 
			
		||||
                if (opt.onchange) {
 | 
			
		||||
                    var value = RED.settings.get(opt.setting);
 | 
			
		||||
                    if (value === null && opt.hasOwnProperty('default')) {
 | 
			
		||||
                    var value = currentEditorSettings.view[opt.setting];
 | 
			
		||||
                    if ((value === null || value === undefined) && opt.hasOwnProperty('default')) {
 | 
			
		||||
                        value = opt.default;
 | 
			
		||||
                        RED.settings.set(opt.setting,value);
 | 
			
		||||
                        currentEditorSettings.view[opt.setting] = value;
 | 
			
		||||
                        editorSettingsChanged = true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var callback = opt.onchange;
 | 
			
		||||
@@ -212,6 +228,9 @@ RED.userSettings = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        if (editorSettingsChanged) {
 | 
			
		||||
            RED.settings.set('editor',currentEditorSettings);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,14 +26,18 @@ RED.utils = (function() {
 | 
			
		||||
    function buildMessageSummaryValue(value) {
 | 
			
		||||
        var result;
 | 
			
		||||
        if (Array.isArray(value)) {
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('array['+value.length+']');
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').text('array['+value.length+']');
 | 
			
		||||
        } else if (value === null) {
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-null">null</span>');
 | 
			
		||||
        } else if (typeof value === 'object') {
 | 
			
		||||
            if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('buffer['+value.length+']');
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').text('buffer['+value.length+']');
 | 
			
		||||
            } else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) {
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('array['+value.length+']');
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').text('array['+value.length+']');
 | 
			
		||||
            } else if (value.hasOwnProperty('type') && value.type === 'function') {
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').text('function');
 | 
			
		||||
            } else if (value.hasOwnProperty('type') && value.type === 'number') {
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-number"></span>').text(value.data);
 | 
			
		||||
            } else {
 | 
			
		||||
                result = $('<span class="debug-message-object-value debug-message-type-meta">object</span>');
 | 
			
		||||
            }
 | 
			
		||||
@@ -45,29 +49,65 @@ RED.utils = (function() {
 | 
			
		||||
                subvalue = sanitize(value);
 | 
			
		||||
            }
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-string"></span>').html('"'+formatString(subvalue)+'"');
 | 
			
		||||
        } else if (typeof value === 'number') {
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-number"></span>').text(""+value);
 | 
			
		||||
        } else {
 | 
			
		||||
            result = $('<span class="debug-message-object-value debug-message-type-other"></span>').text(""+value);
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
    function makeExpandable(el,onexpand,expand) {
 | 
			
		||||
    function makeExpandable(el,onbuild,ontoggle,expand) {
 | 
			
		||||
        el.addClass("debug-message-expandable");
 | 
			
		||||
        el.prop('toggle',function() {
 | 
			
		||||
            return function(state) {
 | 
			
		||||
                var parent = el.parent();
 | 
			
		||||
                if (parent.hasClass('collapsed')) {
 | 
			
		||||
                    if (state) {
 | 
			
		||||
                        if (onbuild && !parent.hasClass('built')) {
 | 
			
		||||
                            onbuild();
 | 
			
		||||
                            parent.addClass('built');
 | 
			
		||||
                        }
 | 
			
		||||
                        parent.removeClass('collapsed');
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (!state) {
 | 
			
		||||
                        parent.addClass('collapsed');
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        el.click(function(e) {
 | 
			
		||||
            var parent = $(this).parent();
 | 
			
		||||
            if (parent.hasClass('collapsed')) {
 | 
			
		||||
                if (onexpand && !parent.hasClass('built')) {
 | 
			
		||||
                    onexpand();
 | 
			
		||||
                    parent.addClass('built');
 | 
			
		||||
            var currentState = !parent.hasClass('collapsed');
 | 
			
		||||
            if ($(this).prop('toggle')(!currentState)) {
 | 
			
		||||
                if (ontoggle) {
 | 
			
		||||
                    ontoggle(!currentState);
 | 
			
		||||
                }
 | 
			
		||||
                parent.removeClass('collapsed');
 | 
			
		||||
            } else {
 | 
			
		||||
                parent.addClass('collapsed');
 | 
			
		||||
            }
 | 
			
		||||
            // if (parent.hasClass('collapsed')) {
 | 
			
		||||
            //     if (onbuild && !parent.hasClass('built')) {
 | 
			
		||||
            //         onbuild();
 | 
			
		||||
            //         parent.addClass('built');
 | 
			
		||||
            //     }
 | 
			
		||||
            //     if (ontoggle) {
 | 
			
		||||
            //         ontoggle(true);
 | 
			
		||||
            //     }
 | 
			
		||||
            //     parent.removeClass('collapsed');
 | 
			
		||||
            // } else {
 | 
			
		||||
            //     parent.addClass('collapsed');
 | 
			
		||||
            //     if (ontoggle) {
 | 
			
		||||
            //         ontoggle(false);
 | 
			
		||||
            //     }
 | 
			
		||||
            // }
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
        });
 | 
			
		||||
        if (expand) {
 | 
			
		||||
            el.click();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var pinnedPaths = {};
 | 
			
		||||
@@ -91,7 +131,7 @@ RED.utils = (function() {
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
            RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue");
 | 
			
		||||
        })
 | 
			
		||||
        if (strippedKey !== '') {
 | 
			
		||||
        if (strippedKey !== undefined && strippedKey !== '') {
 | 
			
		||||
            var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey);
 | 
			
		||||
 | 
			
		||||
            var pinPath = $('<button class="editor-button editor-button-small debug-message-tools-pin"><i class="fa fa-map-pin"></i></button>').appendTo(tools).click(function(e) {
 | 
			
		||||
@@ -137,7 +177,7 @@ RED.utils = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function formatNumber(element,obj,sourceId,path,cycle,initialFormat) {
 | 
			
		||||
        var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path]) || initialFormat || "dec";
 | 
			
		||||
        var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path] && formattedPaths[sourceId][path]['number']) || initialFormat || "dec";
 | 
			
		||||
        if (cycle) {
 | 
			
		||||
            if (format === 'dec') {
 | 
			
		||||
                if ((obj.toString().length===13) && (obj<=2147483647000)) {
 | 
			
		||||
@@ -153,10 +193,12 @@ RED.utils = (function() {
 | 
			
		||||
                format = 'dec';
 | 
			
		||||
            }
 | 
			
		||||
            formattedPaths[sourceId] = formattedPaths[sourceId]||{};
 | 
			
		||||
            formattedPaths[sourceId][path] = format;
 | 
			
		||||
            formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
 | 
			
		||||
            formattedPaths[sourceId][path]['number'] = format;
 | 
			
		||||
        } else if (initialFormat !== undefined){
 | 
			
		||||
            formattedPaths[sourceId] = formattedPaths[sourceId]||{};
 | 
			
		||||
            formattedPaths[sourceId][path] = format;
 | 
			
		||||
            formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
 | 
			
		||||
            formattedPaths[sourceId][path]['number'] = format;
 | 
			
		||||
        }
 | 
			
		||||
        if (format === 'dec') {
 | 
			
		||||
            element.text(""+obj);
 | 
			
		||||
@@ -170,7 +212,7 @@ RED.utils = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function formatBuffer(element,button,sourceId,path,cycle) {
 | 
			
		||||
        var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path]) || "raw";
 | 
			
		||||
        var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path] && formattedPaths[sourceId][path]['buffer']) || "raw";
 | 
			
		||||
        if (cycle) {
 | 
			
		||||
            if (format === 'raw') {
 | 
			
		||||
                format = 'string';
 | 
			
		||||
@@ -178,7 +220,8 @@ RED.utils = (function() {
 | 
			
		||||
                format = 'raw';
 | 
			
		||||
            }
 | 
			
		||||
            formattedPaths[sourceId] = formattedPaths[sourceId]||{};
 | 
			
		||||
            formattedPaths[sourceId][path] = format;
 | 
			
		||||
            formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
 | 
			
		||||
            formattedPaths[sourceId][path]['buffer'] = format;
 | 
			
		||||
        }
 | 
			
		||||
        if (format === 'raw') {
 | 
			
		||||
            button.text('raw');
 | 
			
		||||
@@ -189,10 +232,23 @@ RED.utils = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function buildMessageElement(obj,key,typeHint,hideKey,path,sourceId,rootPath,expandPaths) {
 | 
			
		||||
    function buildMessageElement(obj,options) {
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        var key = options.key;
 | 
			
		||||
        var typeHint = options.typeHint;
 | 
			
		||||
        var hideKey = options.hideKey;
 | 
			
		||||
        var path = options.path;
 | 
			
		||||
        var sourceId = options.sourceId;
 | 
			
		||||
        var rootPath = options.rootPath;
 | 
			
		||||
        var expandPaths = options.expandPaths;
 | 
			
		||||
        var ontoggle = options.ontoggle;
 | 
			
		||||
        var exposeApi = options.exposeApi;
 | 
			
		||||
 | 
			
		||||
        var subElements = {};
 | 
			
		||||
        var i;
 | 
			
		||||
        var e;
 | 
			
		||||
        var entryObj;
 | 
			
		||||
        var expandableHeader;
 | 
			
		||||
        var header;
 | 
			
		||||
        var headerHead;
 | 
			
		||||
        var value;
 | 
			
		||||
@@ -242,22 +298,27 @@ RED.utils = (function() {
 | 
			
		||||
 | 
			
		||||
        var isArray = Array.isArray(obj);
 | 
			
		||||
        var isArrayObject = false;
 | 
			
		||||
        if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__encoded__ && obj.type === 'array') || obj.type === 'Buffer')) {
 | 
			
		||||
        if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__enc__ && obj.type === 'array') || obj.type === 'Buffer')) {
 | 
			
		||||
            isArray = true;
 | 
			
		||||
            isArrayObject = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (obj === null || obj === undefined) {
 | 
			
		||||
            $('<span class="debug-message-type-null">'+obj+'</span>').appendTo(entryObj);
 | 
			
		||||
        } else if (obj.__enc__ && obj.type === 'number') {
 | 
			
		||||
            e = $('<span class="debug-message-type-number debug-message-object-header"></span>').text(obj.data).appendTo(entryObj);
 | 
			
		||||
        } else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
 | 
			
		||||
            e = $('<span class="debug-message-type-meta debug-message-object-header"></span>').text("function").appendTo(entryObj);
 | 
			
		||||
        } else if (typeHint === "internal" || (obj.__enc__ && obj.type === 'internal')) {
 | 
			
		||||
            e = $('<span class="debug-message-type-meta debug-message-object-header"></span>').text("[internal]").appendTo(entryObj);
 | 
			
		||||
        } else if (typeof obj === 'string') {
 | 
			
		||||
            if (/[\t\n\r]/.test(obj)) {
 | 
			
		||||
                element.addClass('collapsed');
 | 
			
		||||
                $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').prependTo(header);
 | 
			
		||||
                makeExpandable(header, function() {
 | 
			
		||||
                    $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html(typeHint||'string').appendTo(header);
 | 
			
		||||
                    $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').text(typeHint||'string').appendTo(header);
 | 
			
		||||
                    var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element);
 | 
			
		||||
                    $('<pre class="debug-message-type-string"></pre>').text(obj).appendTo(row);
 | 
			
		||||
                },checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
                },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
            }
 | 
			
		||||
            e = $('<span class="debug-message-type-string debug-message-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
 | 
			
		||||
            if (/^#[0-9a-f]{6}$/i.test(obj)) {
 | 
			
		||||
@@ -293,7 +354,7 @@ RED.utils = (function() {
 | 
			
		||||
                if (originalLength === undefined) {
 | 
			
		||||
                    originalLength = data.length;
 | 
			
		||||
                }
 | 
			
		||||
                if (data.__encoded__) {
 | 
			
		||||
                if (data.__enc__) {
 | 
			
		||||
                    data = data.data;
 | 
			
		||||
                }
 | 
			
		||||
                type = obj.type.toLowerCase();
 | 
			
		||||
@@ -308,7 +369,7 @@ RED.utils = (function() {
 | 
			
		||||
                element.addClass('debug-message-buffer-raw');
 | 
			
		||||
            }
 | 
			
		||||
            if (key) {
 | 
			
		||||
                headerHead = $('<span class="debug-message-type-meta"></span>').html(typeHint||(type+'['+originalLength+']')).appendTo(entryObj);
 | 
			
		||||
                headerHead = $('<span class="debug-message-type-meta"></span>').text(typeHint||(type+'['+originalLength+']')).appendTo(entryObj);
 | 
			
		||||
            } else {
 | 
			
		||||
                headerHead = $('<span class="debug-message-object-header"></span>').appendTo(entryObj);
 | 
			
		||||
                $('<span>[ </span>').appendTo(headerHead);
 | 
			
		||||
@@ -331,7 +392,7 @@ RED.utils = (function() {
 | 
			
		||||
 | 
			
		||||
                makeExpandable(header,function() {
 | 
			
		||||
                    if (!key) {
 | 
			
		||||
                        headerHead = $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html(typeHint||(type+'['+originalLength+']')).appendTo(header);
 | 
			
		||||
                        headerHead = $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').text(typeHint||(type+'['+originalLength+']')).appendTo(header);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (type === 'buffer') {
 | 
			
		||||
                        var stringRow = $('<div class="debug-message-string-rows"></div>').appendTo(element);
 | 
			
		||||
@@ -344,7 +405,7 @@ RED.utils = (function() {
 | 
			
		||||
                        }
 | 
			
		||||
                        $('<pre class="debug-message-type-string"></pre>').text(stringEncoding).appendTo(sr);
 | 
			
		||||
                        var bufferOpts = $('<span class="debug-message-buffer-opts"></span>').appendTo(headerHead);
 | 
			
		||||
                        var switchFormat = $('<a href="#"></a>').addClass('selected').html('raw').appendTo(bufferOpts).click(function(e) {
 | 
			
		||||
                        var switchFormat = $('<a href="#"></a>').addClass('selected').text('raw').appendTo(bufferOpts).click(function(e) {
 | 
			
		||||
                            e.preventDefault();
 | 
			
		||||
                            e.stopPropagation();
 | 
			
		||||
                            formatBuffer(element,$(this),sourceId,path,true);
 | 
			
		||||
@@ -356,7 +417,20 @@ RED.utils = (function() {
 | 
			
		||||
                    if (fullLength <= 10) {
 | 
			
		||||
                        for (i=0;i<fullLength;i++) {
 | 
			
		||||
                            row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(arrayRows);
 | 
			
		||||
                            buildMessageElement(data[i],""+i,type==='buffer'?'hex':false,false,path+"["+i+"]",sourceId,rootPath,expandPaths).appendTo(row);
 | 
			
		||||
                            subElements[path+"["+i+"]"] = buildMessageElement(
 | 
			
		||||
                                data[i],
 | 
			
		||||
                                {
 | 
			
		||||
                                    key: ""+i,
 | 
			
		||||
                                    typeHint: type==='buffer'?'hex':false,
 | 
			
		||||
                                    hideKey: false,
 | 
			
		||||
                                    path: path+"["+i+"]",
 | 
			
		||||
                                    sourceId: sourceId,
 | 
			
		||||
                                    rootPath: rootPath,
 | 
			
		||||
                                    expandPaths: expandPaths,
 | 
			
		||||
                                    ontoggle: ontoggle,
 | 
			
		||||
                                    exposeApi: exposeApi
 | 
			
		||||
                                }
 | 
			
		||||
                            ).appendTo(row);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        for (i=0;i<fullLength;i+=10) {
 | 
			
		||||
@@ -371,17 +445,35 @@ RED.utils = (function() {
 | 
			
		||||
                                return function() {
 | 
			
		||||
                                    for (var i=min;i<=max;i++) {
 | 
			
		||||
                                        var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(parent);
 | 
			
		||||
                                        buildMessageElement(data[i],""+i,type==='buffer'?'hex':false,false,path+"["+i+"]",sourceId,rootPath,expandPaths).appendTo(row);
 | 
			
		||||
                                        subElements[path+"["+i+"]"] = buildMessageElement(
 | 
			
		||||
                                            data[i],
 | 
			
		||||
                                            {
 | 
			
		||||
                                                key: ""+i,
 | 
			
		||||
                                                typeHint: type==='buffer'?'hex':false,
 | 
			
		||||
                                                hideKey: false,
 | 
			
		||||
                                                path: path+"["+i+"]",
 | 
			
		||||
                                                sourceId: sourceId,
 | 
			
		||||
                                                rootPath: rootPath,
 | 
			
		||||
                                                expandPaths: expandPaths,
 | 
			
		||||
                                                ontoggle: ontoggle,
 | 
			
		||||
                                                exposeApi: exposeApi
 | 
			
		||||
 | 
			
		||||
                                            }
 | 
			
		||||
                                        ).appendTo(row);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            })(),checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
 | 
			
		||||
                            })(),
 | 
			
		||||
                            (function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
 | 
			
		||||
                            checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
 | 
			
		||||
                            $('<span class="debug-message-object-key"></span>').html("["+minRange+" … "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (fullLength < originalLength) {
 | 
			
		||||
                             $('<div class="debug-message-object-entry collapsed"><span class="debug-message-object-key">['+fullLength+' … '+originalLength+']</span></div>').appendTo(arrayRows);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                },checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
                },
 | 
			
		||||
                function(state) {if (ontoggle) { ontoggle(path,state);}},
 | 
			
		||||
                checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
            }
 | 
			
		||||
        } else if (typeof obj === 'object') {
 | 
			
		||||
            element.addClass('collapsed');
 | 
			
		||||
@@ -390,25 +482,43 @@ RED.utils = (function() {
 | 
			
		||||
                $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').prependTo(header);
 | 
			
		||||
                makeExpandable(header, function() {
 | 
			
		||||
                    if (!key) {
 | 
			
		||||
                        $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html('object').appendTo(header);
 | 
			
		||||
                        $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').text('object').appendTo(header);
 | 
			
		||||
                    }
 | 
			
		||||
                    for (i=0;i<keys.length;i++) {
 | 
			
		||||
                        var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element);
 | 
			
		||||
                        var newPath = path;
 | 
			
		||||
                        if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(keys[i])) {
 | 
			
		||||
                            newPath += (newPath.length > 0?".":"")+keys[i];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
 | 
			
		||||
                        if (newPath !== undefined) {
 | 
			
		||||
                            if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(keys[i])) {
 | 
			
		||||
                                newPath += (newPath.length > 0?".":"")+keys[i];
 | 
			
		||||
                            } else {
 | 
			
		||||
                                newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        buildMessageElement(obj[keys[i]],keys[i],false,false,newPath,sourceId,rootPath,expandPaths).appendTo(row);
 | 
			
		||||
                        subElements[newPath] = buildMessageElement(
 | 
			
		||||
                            obj[keys[i]],
 | 
			
		||||
                            {
 | 
			
		||||
                                key: keys[i],
 | 
			
		||||
                                typeHint: false,
 | 
			
		||||
                                hideKey: false,
 | 
			
		||||
                                path: newPath,
 | 
			
		||||
                                sourceId: sourceId,
 | 
			
		||||
                                rootPath: rootPath,
 | 
			
		||||
                                expandPaths: expandPaths,
 | 
			
		||||
                                ontoggle: ontoggle,
 | 
			
		||||
                                exposeApi: exposeApi
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        ).appendTo(row);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (keys.length === 0) {
 | 
			
		||||
                        $('<div class="debug-message-object-entry debug-message-type-meta collapsed"></div>').text("empty").appendTo(element);
 | 
			
		||||
                    }
 | 
			
		||||
                },checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
                },
 | 
			
		||||
                function(state) {if (ontoggle) { ontoggle(path,state);}},
 | 
			
		||||
                checkExpanded(strippedKey,expandPaths));
 | 
			
		||||
            }
 | 
			
		||||
            if (key) {
 | 
			
		||||
                $('<span class="debug-message-type-meta"></span>').html('object').appendTo(entryObj);
 | 
			
		||||
                $('<span class="debug-message-type-meta"></span>').text('object').appendTo(entryObj);
 | 
			
		||||
            } else {
 | 
			
		||||
                headerHead = $('<span class="debug-message-object-header"></span>').appendTo(entryObj);
 | 
			
		||||
                $('<span>{ </span>').appendTo(headerHead);
 | 
			
		||||
@@ -432,6 +542,28 @@ RED.utils = (function() {
 | 
			
		||||
        } else {
 | 
			
		||||
            $('<span class="debug-message-type-other"></span>').text(""+obj).appendTo(entryObj);
 | 
			
		||||
        }
 | 
			
		||||
        if (exposeApi) {
 | 
			
		||||
            element.prop('expand', function() { return function(targetPath, state) {
 | 
			
		||||
                if (path === targetPath) {
 | 
			
		||||
                    if (header.prop('toggle')) {
 | 
			
		||||
                        header.prop('toggle')(state);
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (subElements[targetPath] && subElements[targetPath].prop('expand') ) {
 | 
			
		||||
                    subElements[targetPath].prop('expand')(targetPath,state);
 | 
			
		||||
                } else {
 | 
			
		||||
                    for (var p in subElements) {
 | 
			
		||||
                        if (subElements.hasOwnProperty(p)) {
 | 
			
		||||
                            if (targetPath.indexOf(p) === 0) {
 | 
			
		||||
                                if (subElements[p].prop('expand') ) {
 | 
			
		||||
                                    subElements[p].prop('expand')(targetPath,state);
 | 
			
		||||
                                }
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }});
 | 
			
		||||
        }
 | 
			
		||||
        return element;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -571,16 +703,25 @@ RED.utils = (function() {
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getNodeIcon(def,node) {
 | 
			
		||||
        if (def.category === 'config') {
 | 
			
		||||
            return "icons/node-red/cog.png"
 | 
			
		||||
        } else if (node && node.type === 'tab') {
 | 
			
		||||
            return "icons/node-red/subflow.png"
 | 
			
		||||
        } else if (node && node.type === 'unknown') {
 | 
			
		||||
            return "icons/node-red/alert.png"
 | 
			
		||||
    function separateIconPath(icon) {
 | 
			
		||||
        var result = {module: "", file: ""};
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            var index = icon.indexOf('/');
 | 
			
		||||
            if (index !== -1) {
 | 
			
		||||
                result.module = icon.slice(0, index);
 | 
			
		||||
                result.file = icon.slice(index + 1);
 | 
			
		||||
            } else {
 | 
			
		||||
                result.file = icon;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getDefaultNodeIcon(def,node) {
 | 
			
		||||
        var icon_url;
 | 
			
		||||
        if (typeof def.icon === "function") {
 | 
			
		||||
        if (node && node.type === "subflow") {
 | 
			
		||||
            icon_url = "node-red/subflow.png";
 | 
			
		||||
        } else if (typeof def.icon === "function") {
 | 
			
		||||
            try {
 | 
			
		||||
                icon_url = def.icon.call(node);
 | 
			
		||||
            } catch(err) {
 | 
			
		||||
@@ -590,7 +731,50 @@ RED.utils = (function() {
 | 
			
		||||
        } else {
 | 
			
		||||
            icon_url = def.icon;
 | 
			
		||||
        }
 | 
			
		||||
        return "icons/"+def.set.module+"/"+icon_url;
 | 
			
		||||
 | 
			
		||||
        var iconPath = separateIconPath(icon_url);
 | 
			
		||||
        if (!iconPath.module) {
 | 
			
		||||
            if (def.set) {
 | 
			
		||||
                iconPath.module = def.set.module;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Handle subflow instance nodes that don't have def.set
 | 
			
		||||
                iconPath.module = "node-red";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return iconPath;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function isIconExists(iconPath) {
 | 
			
		||||
        var iconSets = RED.nodes.getIconSets();
 | 
			
		||||
        var iconFileList = iconSets[iconPath.module];
 | 
			
		||||
        if (iconFileList && iconFileList.indexOf(iconPath.file) !== -1) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getNodeIcon(def,node) {
 | 
			
		||||
        if (def.category === 'config') {
 | 
			
		||||
            return "icons/node-red/cog.png"
 | 
			
		||||
        } else if (node && node.type === 'tab') {
 | 
			
		||||
            return "icons/node-red/subflow.png"
 | 
			
		||||
        } else if (node && node.type === 'unknown') {
 | 
			
		||||
            return "icons/node-red/alert.png"
 | 
			
		||||
        } else if (node && node.icon) {
 | 
			
		||||
            var iconPath = separateIconPath(node.icon);
 | 
			
		||||
            if (isIconExists(iconPath)) {
 | 
			
		||||
                return "icons/" + node.icon;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var iconPath = getDefaultNodeIcon(def, node);
 | 
			
		||||
        if (def.category === 'subflows') {
 | 
			
		||||
            if (!isIconExists(iconPath)) {
 | 
			
		||||
                return "icons/node-red/subflow.png";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return "icons/"+iconPath.module+"/"+iconPath.file;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getNodeLabel(node,defaultLabel) {
 | 
			
		||||
@@ -610,12 +794,102 @@ RED.utils = (function() {
 | 
			
		||||
        return RED.text.bidi.enforceTextDirectionWithUCC(l);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var nodeColorCache = {};
 | 
			
		||||
    function getNodeColor(type, def) {
 | 
			
		||||
        var result = def.color;
 | 
			
		||||
        var paletteTheme = RED.settings.theme('palette.theme') || [];
 | 
			
		||||
        if (paletteTheme.length > 0) {
 | 
			
		||||
            if (!nodeColorCache.hasOwnProperty(type)) {
 | 
			
		||||
                nodeColorCache[type] = def.color;
 | 
			
		||||
                var l = paletteTheme.length;
 | 
			
		||||
                for (var i = 0; i < l; i++ ){
 | 
			
		||||
                    var themeRule = paletteTheme[i];
 | 
			
		||||
                    if (themeRule.hasOwnProperty('category')) {
 | 
			
		||||
                        if (!themeRule.hasOwnProperty('_category')) {
 | 
			
		||||
                            themeRule._category = new RegExp(themeRule.category);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (!themeRule._category.test(def.category)) {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (themeRule.hasOwnProperty('type')) {
 | 
			
		||||
                        if (!themeRule.hasOwnProperty('_type')) {
 | 
			
		||||
                            themeRule._type = new RegExp(themeRule.type);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (!themeRule._type.test(type)) {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    nodeColorCache[type] = themeRule.color || def.color;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            result = nodeColorCache[type];
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addSpinnerOverlay(container,contain) {
 | 
			
		||||
        var spinner = $('<div class="projects-dialog-spinner "><img src="red/images/spin.svg"/></div>').appendTo(container);
 | 
			
		||||
        if (contain) {
 | 
			
		||||
            spinner.addClass('projects-dialog-spinner-contain');
 | 
			
		||||
        }
 | 
			
		||||
        return spinner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function decodeObject(payload,format) {
 | 
			
		||||
        if ((format === 'number') && (payload === "NaN")) {
 | 
			
		||||
            payload = Number.NaN;
 | 
			
		||||
        } else if ((format === 'number') && (payload === "Infinity")) {
 | 
			
		||||
            payload = Infinity;
 | 
			
		||||
        } else if ((format === 'number') && (payload === "-Infinity")) {
 | 
			
		||||
            payload = -Infinity;
 | 
			
		||||
        } else if (format === 'Object' || /^array/.test(format) || format === 'boolean' || format === 'number' ) {
 | 
			
		||||
            payload = JSON.parse(payload);
 | 
			
		||||
        } else if (/error/i.test(format)) {
 | 
			
		||||
            payload = JSON.parse(payload);
 | 
			
		||||
            payload = (payload.name?payload.name+": ":"")+payload.message;
 | 
			
		||||
        } else if (format === 'null') {
 | 
			
		||||
            payload = null;
 | 
			
		||||
        } else if (format === 'undefined') {
 | 
			
		||||
            payload = undefined;
 | 
			
		||||
        } else if (/^buffer/.test(format)) {
 | 
			
		||||
            var buffer = payload;
 | 
			
		||||
            payload = [];
 | 
			
		||||
            for (var c = 0; c < buffer.length; c += 2) {
 | 
			
		||||
                payload.push(parseInt(buffer.substr(c, 2), 16));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return payload;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function parseContextKey(key) {
 | 
			
		||||
        var parts = {};
 | 
			
		||||
        var m = /^#:\((\S+?)\)::(.*)$/.exec(key);
 | 
			
		||||
        if (m) {
 | 
			
		||||
            parts.store = m[1];
 | 
			
		||||
            parts.key = m[2];
 | 
			
		||||
        } else {
 | 
			
		||||
            parts.key = key;
 | 
			
		||||
            if (RED.settings.context) {
 | 
			
		||||
                parts.store = RED.settings.context.default;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return parts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        createObjectElement: buildMessageElement,
 | 
			
		||||
        getMessageProperty: getMessageProperty,
 | 
			
		||||
        normalisePropertyExpression: normalisePropertyExpression,
 | 
			
		||||
        validatePropertyExpression: validatePropertyExpression,
 | 
			
		||||
        separateIconPath: separateIconPath,
 | 
			
		||||
        getDefaultNodeIcon: getDefaultNodeIcon,
 | 
			
		||||
        getNodeIcon: getNodeIcon,
 | 
			
		||||
        getNodeLabel: getNodeLabel,
 | 
			
		||||
        getNodeColor: getNodeColor,
 | 
			
		||||
        addSpinnerOverlay: addSpinnerOverlay,
 | 
			
		||||
        decodeObject: decodeObject,
 | 
			
		||||
        parseContextKey: parseContextKey
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								editor/js/ui/view-navigator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								editor/js/ui/view-navigator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright 2016 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 RED.view.navigator = (function() {
 | 
			
		||||
 | 
			
		||||
     var nav_scale = 25;
 | 
			
		||||
     var nav_width = 5000/nav_scale;
 | 
			
		||||
     var nav_height = 5000/nav_scale;
 | 
			
		||||
 | 
			
		||||
     var navContainer;
 | 
			
		||||
     var navBox;
 | 
			
		||||
     var navBorder;
 | 
			
		||||
     var navVis;
 | 
			
		||||
     var scrollPos;
 | 
			
		||||
     var scaleFactor;
 | 
			
		||||
     var chartSize;
 | 
			
		||||
     var dimensions;
 | 
			
		||||
     var isDragging;
 | 
			
		||||
     var isShowing = false;
 | 
			
		||||
 | 
			
		||||
     function refreshNodes() {
 | 
			
		||||
         if (!isShowing) {
 | 
			
		||||
             return;
 | 
			
		||||
         }
 | 
			
		||||
         var navNode = navVis.selectAll(".navnode").data(RED.view.getActiveNodes(),function(d){return d.id});
 | 
			
		||||
         navNode.exit().remove();
 | 
			
		||||
         navNode.enter().insert("rect")
 | 
			
		||||
             .attr('class','navnode')
 | 
			
		||||
             .attr("pointer-events", "none");
 | 
			
		||||
         navNode.each(function(d) {
 | 
			
		||||
             d3.select(this).attr("x",function(d) { return (d.x-d.w/2)/nav_scale })
 | 
			
		||||
             .attr("y",function(d) { return (d.y-d.h/2)/nav_scale })
 | 
			
		||||
             .attr("width",function(d) { return Math.max(9,d.w/nav_scale) })
 | 
			
		||||
             .attr("height",function(d) { return Math.max(3,d.h/nav_scale) })
 | 
			
		||||
             .attr("fill",function(d) { return RED.utils.getNodeColor(d.type,d._def);})
 | 
			
		||||
         });
 | 
			
		||||
     }
 | 
			
		||||
     function onScroll() {
 | 
			
		||||
         if (!isDragging) {
 | 
			
		||||
             resizeNavBorder();
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
     function resizeNavBorder() {
 | 
			
		||||
         if (navBorder) {
 | 
			
		||||
             scaleFactor = RED.view.scale();
 | 
			
		||||
             chartSize = [ $("#chart").width(), $("#chart").height()];
 | 
			
		||||
             scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
 | 
			
		||||
             navBorder.attr('x',scrollPos[0]/nav_scale)
 | 
			
		||||
                      .attr('y',scrollPos[1]/nav_scale)
 | 
			
		||||
                      .attr('width',chartSize[0]/nav_scale/scaleFactor)
 | 
			
		||||
                      .attr('height',chartSize[1]/nav_scale/scaleFactor)
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
     function toggle() {
 | 
			
		||||
         if (!isShowing) {
 | 
			
		||||
             isShowing = true;
 | 
			
		||||
             $("#btn-navigate").addClass("selected");
 | 
			
		||||
             resizeNavBorder();
 | 
			
		||||
             refreshNodes();
 | 
			
		||||
             $("#chart").on("scroll",onScroll);
 | 
			
		||||
             navContainer.fadeIn(200);
 | 
			
		||||
         } else {
 | 
			
		||||
             isShowing = false;
 | 
			
		||||
             navContainer.fadeOut(100);
 | 
			
		||||
             $("#chart").off("scroll",onScroll);
 | 
			
		||||
             $("#btn-navigate").removeClass("selected");
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     return {
 | 
			
		||||
         init: function() {
 | 
			
		||||
 | 
			
		||||
             $(window).resize(resizeNavBorder);
 | 
			
		||||
             RED.events.on("sidebar:resize",resizeNavBorder);
 | 
			
		||||
             RED.actions.add("core:toggle-navigator",toggle);
 | 
			
		||||
             var hideTimeout;
 | 
			
		||||
 | 
			
		||||
             navContainer = $('<div>').css({
 | 
			
		||||
                 "position":"absolute",
 | 
			
		||||
                 "bottom":$("#workspace-footer").height(),
 | 
			
		||||
                 "right":0,
 | 
			
		||||
                 zIndex: 1
 | 
			
		||||
             }).appendTo("#workspace").hide();
 | 
			
		||||
 | 
			
		||||
             navBox = d3.select(navContainer[0])
 | 
			
		||||
                 .append("svg:svg")
 | 
			
		||||
                 .attr("width", nav_width)
 | 
			
		||||
                 .attr("height", nav_height)
 | 
			
		||||
                 .attr("pointer-events", "all")
 | 
			
		||||
                 .style({
 | 
			
		||||
                     position: "absolute",
 | 
			
		||||
                     bottom: 0,
 | 
			
		||||
                     right:0,
 | 
			
		||||
                     zIndex: 101,
 | 
			
		||||
                     "border-left": "1px solid #ccc",
 | 
			
		||||
                     "border-top": "1px solid #ccc",
 | 
			
		||||
                     background: "rgba(245,245,245,0.5)",
 | 
			
		||||
                     "box-shadow": "-1px 0 3px rgba(0,0,0,0.1)"
 | 
			
		||||
                 });
 | 
			
		||||
 | 
			
		||||
             navBox.append("rect").attr("x",0).attr("y",0).attr("width",nav_width).attr("height",nav_height).style({
 | 
			
		||||
                 fill:"none",
 | 
			
		||||
                 stroke:"none",
 | 
			
		||||
                 pointerEvents:"all"
 | 
			
		||||
             }).on("mousedown", function() {
 | 
			
		||||
                 // Update these in case they have changed
 | 
			
		||||
                 scaleFactor = RED.view.scale();
 | 
			
		||||
                 chartSize = [ $("#chart").width(), $("#chart").height()];
 | 
			
		||||
                 dimensions = [chartSize[0]/nav_scale/scaleFactor, chartSize[1]/nav_scale/scaleFactor];
 | 
			
		||||
                 var newX = Math.max(0,Math.min(d3.event.offsetX+dimensions[0]/2,nav_width)-dimensions[0]);
 | 
			
		||||
                 var newY = Math.max(0,Math.min(d3.event.offsetY+dimensions[1]/2,nav_height)-dimensions[1]);
 | 
			
		||||
                 navBorder.attr('x',newX).attr('y',newY);
 | 
			
		||||
                 isDragging = true;
 | 
			
		||||
                 $("#chart").scrollLeft(newX*nav_scale*scaleFactor);
 | 
			
		||||
                 $("#chart").scrollTop(newY*nav_scale*scaleFactor);
 | 
			
		||||
             }).on("mousemove", function() {
 | 
			
		||||
                 if (!isDragging) { return }
 | 
			
		||||
                 if (d3.event.buttons === 0) {
 | 
			
		||||
                     isDragging = false;
 | 
			
		||||
                     return;
 | 
			
		||||
                 }
 | 
			
		||||
                 var newX = Math.max(0,Math.min(d3.event.offsetX+dimensions[0]/2,nav_width)-dimensions[0]);
 | 
			
		||||
                 var newY = Math.max(0,Math.min(d3.event.offsetY+dimensions[1]/2,nav_height)-dimensions[1]);
 | 
			
		||||
                 navBorder.attr('x',newX).attr('y',newY);
 | 
			
		||||
                 $("#chart").scrollLeft(newX*nav_scale*scaleFactor);
 | 
			
		||||
                 $("#chart").scrollTop(newY*nav_scale*scaleFactor);
 | 
			
		||||
             }).on("mouseup", function() {
 | 
			
		||||
                 isDragging = false;
 | 
			
		||||
             })
 | 
			
		||||
 | 
			
		||||
             navBorder = navBox.append("rect")
 | 
			
		||||
                 .attr("stroke-dasharray","5,5")
 | 
			
		||||
                 .attr("pointer-events", "none")
 | 
			
		||||
                 .style({
 | 
			
		||||
                     stroke: "#999",
 | 
			
		||||
                     strokeWidth: 1,
 | 
			
		||||
                     fill: "white",
 | 
			
		||||
                 });
 | 
			
		||||
 | 
			
		||||
             navVis = navBox.append("svg:g")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            $("#btn-navigate").click(function(evt) {
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                toggle();
 | 
			
		||||
            })
 | 
			
		||||
        },
 | 
			
		||||
        refresh: refreshNodes,
 | 
			
		||||
        resize: resizeNavBorder,
 | 
			
		||||
        toggle: toggle
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
})();
 | 
			
		||||
@@ -58,7 +58,10 @@ RED.view = (function() {
 | 
			
		||||
        lastClickNode = null,
 | 
			
		||||
        dblClickPrimed = null,
 | 
			
		||||
        clickTime = 0,
 | 
			
		||||
        clickElapsed = 0;
 | 
			
		||||
        clickElapsed = 0,
 | 
			
		||||
        scroll_position = [],
 | 
			
		||||
        quickAddActive = false,
 | 
			
		||||
        quickAddLink = null;
 | 
			
		||||
 | 
			
		||||
    var clipboard = "";
 | 
			
		||||
 | 
			
		||||
@@ -73,6 +76,8 @@ RED.view = (function() {
 | 
			
		||||
    var PORT_TYPE_INPUT = 1;
 | 
			
		||||
    var PORT_TYPE_OUTPUT = 0;
 | 
			
		||||
 | 
			
		||||
    var chart = $("#chart");
 | 
			
		||||
 | 
			
		||||
    var outer = d3.select("#chart")
 | 
			
		||||
        .append("svg:svg")
 | 
			
		||||
        .attr("width", space_width)
 | 
			
		||||
@@ -81,6 +86,9 @@ RED.view = (function() {
 | 
			
		||||
        .style("cursor","crosshair")
 | 
			
		||||
        .on("mousedown", function() {
 | 
			
		||||
            focusView();
 | 
			
		||||
        })
 | 
			
		||||
        .on("contextmenu", function(){
 | 
			
		||||
            d3.event.preventDefault();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    var vis = outer
 | 
			
		||||
@@ -91,6 +99,16 @@ RED.view = (function() {
 | 
			
		||||
        .on("mousemove", canvasMouseMove)
 | 
			
		||||
        .on("mousedown", canvasMouseDown)
 | 
			
		||||
        .on("mouseup", canvasMouseUp)
 | 
			
		||||
        .on("mouseenter", function() {
 | 
			
		||||
            if (lasso) {
 | 
			
		||||
                if (d3.event.buttons !== 1) {
 | 
			
		||||
                    lasso.remove();
 | 
			
		||||
                    lasso = null;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (mouse_mode === RED.state.PANNING && d3.event.buttons !== 4) {
 | 
			
		||||
                resetMouseVars();
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .on("touchend", function() {
 | 
			
		||||
            clearTimeout(touchStartTime);
 | 
			
		||||
            touchStartTime = null;
 | 
			
		||||
@@ -280,7 +298,6 @@ RED.view = (function() {
 | 
			
		||||
    function init() {
 | 
			
		||||
 | 
			
		||||
        RED.events.on("workspace:change",function(event) {
 | 
			
		||||
            var chart = $("#chart");
 | 
			
		||||
            if (event.old !== 0) {
 | 
			
		||||
                workspaceScrollPositions[event.old] = {
 | 
			
		||||
                    left:chart.scrollLeft(),
 | 
			
		||||
@@ -317,6 +334,8 @@ RED.view = (function() {
 | 
			
		||||
            redraw();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.view.navigator.init();
 | 
			
		||||
 | 
			
		||||
        $("#btn-zoom-out").click(function() {zoomOut();});
 | 
			
		||||
        $("#btn-zoom-zero").click(function() {zoomZero();});
 | 
			
		||||
        $("#btn-zoom-in").click(function() {zoomIn();});
 | 
			
		||||
@@ -396,10 +415,10 @@ RED.view = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        $("#chart").focus(function() {
 | 
			
		||||
            $("#workspace-tabs").addClass("workspace-focussed")
 | 
			
		||||
            $("#workspace-tabs").addClass("workspace-focussed");
 | 
			
		||||
        });
 | 
			
		||||
        $("#chart").blur(function() {
 | 
			
		||||
            $("#workspace-tabs").removeClass("workspace-focussed")
 | 
			
		||||
            $("#workspace-tabs").removeClass("workspace-focussed");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
 | 
			
		||||
@@ -445,6 +464,82 @@ RED.view = (function() {
 | 
			
		||||
        RED.actions.add("core:step-selection-left", function() { moveSelection(-20,0);});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function generateLinkPath(origX,origY, destX, destY, sc) {
 | 
			
		||||
        var dy = destY-origY;
 | 
			
		||||
        var dx = destX-origX;
 | 
			
		||||
        var delta = Math.sqrt(dy*dy+dx*dx);
 | 
			
		||||
        var scale = lineCurveScale;
 | 
			
		||||
        var scaleY = 0;
 | 
			
		||||
        if (dx*sc > 0) {
 | 
			
		||||
            if (delta < node_width) {
 | 
			
		||||
                scale = 0.75-0.75*((node_width-delta)/node_width);
 | 
			
		||||
                // scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
 | 
			
		||||
                // if (Math.abs(dy) < 3*node_height) {
 | 
			
		||||
                //     scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
 | 
			
		||||
                // }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            scale = 0.4-0.2*(Math.max(0,(node_width-Math.min(Math.abs(dx),Math.abs(dy)))/node_width));
 | 
			
		||||
        }
 | 
			
		||||
        if (dx*sc > 0) {
 | 
			
		||||
            return "M "+origX+" "+origY+
 | 
			
		||||
                " C "+(origX+sc*(node_width*scale))+" "+(origY+scaleY*node_height)+" "+
 | 
			
		||||
                (destX-sc*(scale)*node_width)+" "+(destY-scaleY*node_height)+" "+
 | 
			
		||||
                destX+" "+destY
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            var midX = Math.floor(destX-dx/2);
 | 
			
		||||
            var midY = Math.floor(destY-dy/2);
 | 
			
		||||
            //
 | 
			
		||||
            if (dy === 0) {
 | 
			
		||||
                midY = destY + node_height;
 | 
			
		||||
            }
 | 
			
		||||
            var cp_height = node_height/2;
 | 
			
		||||
            var y1 = (destY + midY)/2
 | 
			
		||||
            var topX =origX + sc*node_width*scale;
 | 
			
		||||
            var topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height);
 | 
			
		||||
            var bottomX = destX - sc*node_width*scale;
 | 
			
		||||
            var bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height);
 | 
			
		||||
            var x1 = (origX+topX)/2;
 | 
			
		||||
            var scy = dy>0?1:-1;
 | 
			
		||||
            var cp = [
 | 
			
		||||
                // Orig -> Top
 | 
			
		||||
                [x1,origY],
 | 
			
		||||
                [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)],
 | 
			
		||||
                // Top -> Mid
 | 
			
		||||
                // [Mirror previous cp]
 | 
			
		||||
                [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)],
 | 
			
		||||
                // Mid -> Bottom
 | 
			
		||||
                // [Mirror previous cp]
 | 
			
		||||
                [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)],
 | 
			
		||||
                // Bottom -> Dest
 | 
			
		||||
                // [Mirror previous cp]
 | 
			
		||||
                [(destX+bottomX)/2,destY]
 | 
			
		||||
            ];
 | 
			
		||||
            if (cp[2][1] === topY+scy*cp_height) {
 | 
			
		||||
                if (Math.abs(dy) < cp_height*10) {
 | 
			
		||||
                    cp[1][1] = topY-scy*cp_height/2;
 | 
			
		||||
                    cp[3][1] = bottomY-scy*cp_height/2;
 | 
			
		||||
                }
 | 
			
		||||
                cp[2][0] = topX;
 | 
			
		||||
            }
 | 
			
		||||
            return "M "+origX+" "+origY+
 | 
			
		||||
                " C "+
 | 
			
		||||
                   cp[0][0]+" "+cp[0][1]+" "+
 | 
			
		||||
                   cp[1][0]+" "+cp[1][1]+" "+
 | 
			
		||||
                   topX+" "+topY+
 | 
			
		||||
                " S "+
 | 
			
		||||
                   cp[2][0]+" "+cp[2][1]+" "+
 | 
			
		||||
                   midX+" "+midY+
 | 
			
		||||
               " S "+
 | 
			
		||||
                  cp[3][0]+" "+cp[3][1]+" "+
 | 
			
		||||
                  bottomX+" "+bottomY+
 | 
			
		||||
                " S "+
 | 
			
		||||
                    cp[4][0]+" "+cp[4][1]+" "+
 | 
			
		||||
                    destX+" "+destY
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function addNode(type,x,y) {
 | 
			
		||||
        var m = /^subflow:(.+)$/.exec(type);
 | 
			
		||||
@@ -482,11 +577,12 @@ RED.view = (function() {
 | 
			
		||||
                try {
 | 
			
		||||
                    nn._def.onadd.call(nn);
 | 
			
		||||
                } catch(err) {
 | 
			
		||||
                    console.log("onadd:",err);
 | 
			
		||||
                    console.log("Definition error: "+nn.type+".onadd:",err);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            var subflow = RED.nodes.subflow(m[1]);
 | 
			
		||||
            nn.name = "";
 | 
			
		||||
            nn.inputs = subflow.in.length;
 | 
			
		||||
            nn.outputs = subflow.out.length;
 | 
			
		||||
        }
 | 
			
		||||
@@ -522,6 +618,15 @@ RED.view = (function() {
 | 
			
		||||
    function canvasMouseDown() {
 | 
			
		||||
        var point;
 | 
			
		||||
 | 
			
		||||
        if (d3.event.button === 1) {
 | 
			
		||||
            // Middle Click pan
 | 
			
		||||
            mouse_mode = RED.state.PANNING;
 | 
			
		||||
            mouse_position = [d3.event.pageX,d3.event.pageY]
 | 
			
		||||
            scroll_position = [chart.scrollLeft(),chart.scrollTop()];
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!mousedown_node && !mousedown_link) {
 | 
			
		||||
            selected_link = null;
 | 
			
		||||
            updateSelection();
 | 
			
		||||
@@ -542,11 +647,16 @@ RED.view = (function() {
 | 
			
		||||
                    mouse_mode = RED.state.QUICK_JOINING;
 | 
			
		||||
                    $(window).on('keyup',disableQuickJoinEventHandler);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                quickAddActive = true;
 | 
			
		||||
                RED.typeSearch.show({
 | 
			
		||||
                    x:d3.event.clientX-mainPos.left-node_width/2,
 | 
			
		||||
                    y:d3.event.clientY-mainPos.top-node_height/2,
 | 
			
		||||
                    cancel: function() {
 | 
			
		||||
                        quickAddActive = false;
 | 
			
		||||
                        resetMouseVars();
 | 
			
		||||
                    },
 | 
			
		||||
                    add: function(type) {
 | 
			
		||||
                        quickAddActive = false;
 | 
			
		||||
                        var result = addNode(type);
 | 
			
		||||
                        if (!result) {
 | 
			
		||||
                            return;
 | 
			
		||||
@@ -555,11 +665,10 @@ RED.view = (function() {
 | 
			
		||||
                        var historyEvent = result.historyEvent;
 | 
			
		||||
                        nn.x = point[0];
 | 
			
		||||
                        nn.y = point[1];
 | 
			
		||||
                        if (mouse_mode === RED.state.QUICK_JOINING) {
 | 
			
		||||
                            if (drag_lines.length > 0) {
 | 
			
		||||
                                var drag_line = drag_lines[0];
 | 
			
		||||
                        if (mouse_mode === RED.state.QUICK_JOINING || quickAddLink) {
 | 
			
		||||
                            if (quickAddLink || drag_lines.length > 0) {
 | 
			
		||||
                                var drag_line = quickAddLink||drag_lines[0];
 | 
			
		||||
                                var src = null,dst,src_port;
 | 
			
		||||
 | 
			
		||||
                                if (drag_line.portType === PORT_TYPE_OUTPUT && nn.inputs > 0) {
 | 
			
		||||
                                    src = drag_line.node;
 | 
			
		||||
                                    src_port = drag_line.port;
 | 
			
		||||
@@ -574,9 +683,9 @@ RED.view = (function() {
 | 
			
		||||
                                    RED.nodes.addLink(link);
 | 
			
		||||
                                    historyEvent.links = [link];
 | 
			
		||||
                                    hideDragLines();
 | 
			
		||||
                                    if (drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) {
 | 
			
		||||
                                    if (!quickAddLink && drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) {
 | 
			
		||||
                                        showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]);
 | 
			
		||||
                                    } else if (drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) {
 | 
			
		||||
                                    } else if (!quickAddLink && drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) {
 | 
			
		||||
                                        showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        resetMouseVars();
 | 
			
		||||
@@ -594,9 +703,9 @@ RED.view = (function() {
 | 
			
		||||
                                    resetMouseVars();
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            quickAddLink = null;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        RED.history.push(historyEvent);
 | 
			
		||||
                        RED.nodes.add(nn);
 | 
			
		||||
                        RED.editor.validateNode(nn);
 | 
			
		||||
@@ -637,7 +746,6 @@ RED.view = (function() {
 | 
			
		||||
    function canvasMouseMove() {
 | 
			
		||||
        var i;
 | 
			
		||||
        var node;
 | 
			
		||||
        mouse_position = d3.touches(this)[0]||d3.mouse(this);
 | 
			
		||||
        // Prevent touch scrolling...
 | 
			
		||||
        //if (d3.touches(this)[0]) {
 | 
			
		||||
        //    d3.event.preventDefault();
 | 
			
		||||
@@ -648,6 +756,22 @@ RED.view = (function() {
 | 
			
		||||
        //if (point[0]-container.scrollLeft < 30 && container.scrollLeft > 0) { container.scrollLeft -= 15; }
 | 
			
		||||
        //console.log(d3.mouse(this),container.offsetWidth,container.offsetHeight,container.scrollLeft,container.scrollTop);
 | 
			
		||||
 | 
			
		||||
        if (mouse_mode === RED.state.PANNING) {
 | 
			
		||||
 | 
			
		||||
            var pos = [d3.event.pageX,d3.event.pageY];
 | 
			
		||||
            var deltaPos = [
 | 
			
		||||
                mouse_position[0]-pos[0],
 | 
			
		||||
                mouse_position[1]-pos[1]
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            chart.scrollLeft(scroll_position[0]+deltaPos[0])
 | 
			
		||||
            chart.scrollTop(scroll_position[1]+deltaPos[1])
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mouse_position = d3.touches(this)[0]||d3.mouse(this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (lasso) {
 | 
			
		||||
            var ox = parseInt(lasso.attr("ox"));
 | 
			
		||||
            var oy = parseInt(lasso.attr("oy"));
 | 
			
		||||
@@ -683,7 +807,7 @@ RED.view = (function() {
 | 
			
		||||
        var mousePos;
 | 
			
		||||
        if (mouse_mode == RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) {
 | 
			
		||||
            // update drag line
 | 
			
		||||
            if (drag_lines.length === 0) {
 | 
			
		||||
            if (drag_lines.length === 0 && mousedown_port_type !== null) {
 | 
			
		||||
                if (d3.event.shiftKey) {
 | 
			
		||||
                    // Get all the wires we need to detach.
 | 
			
		||||
                    var links = [];
 | 
			
		||||
@@ -746,28 +870,7 @@ RED.view = (function() {
 | 
			
		||||
 | 
			
		||||
                var sc = (drag_line.portType === PORT_TYPE_OUTPUT)?1:-1;
 | 
			
		||||
 | 
			
		||||
                var dy = mousePos[1]-(drag_line.node.y+portY);
 | 
			
		||||
                var dx = mousePos[0]-(drag_line.node.x+sc*drag_line.node.w/2);
 | 
			
		||||
                var delta = Math.sqrt(dy*dy+dx*dx);
 | 
			
		||||
                var scale = lineCurveScale;
 | 
			
		||||
                var scaleY = 0;
 | 
			
		||||
 | 
			
		||||
                if (delta < node_width) {
 | 
			
		||||
                    scale = 0.75-0.75*((node_width-delta)/node_width);
 | 
			
		||||
                }
 | 
			
		||||
                if (dx*sc < 0) {
 | 
			
		||||
                    scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
 | 
			
		||||
                    if (Math.abs(dy) < 3*node_height) {
 | 
			
		||||
                        scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                drag_line.el.attr("d",
 | 
			
		||||
                    "M "+(drag_line.node.x+sc*drag_line.node.w/2)+" "+(drag_line.node.y+portY)+
 | 
			
		||||
                    " C "+(drag_line.node.x+sc*(drag_line.node.w/2+node_width*scale))+" "+(drag_line.node.y+portY+scaleY*node_height)+" "+
 | 
			
		||||
                    (mousePos[0]-sc*(scale)*node_width)+" "+(mousePos[1]-scaleY*node_height)+" "+
 | 
			
		||||
                    mousePos[0]+" "+mousePos[1]
 | 
			
		||||
                    );
 | 
			
		||||
                drag_line.el.attr("d",generateLinkPath(drag_line.node.x+sc*drag_line.node.w/2,drag_line.node.y+portY,mousePos[0],mousePos[1],sc));
 | 
			
		||||
            }
 | 
			
		||||
            d3.event.preventDefault();
 | 
			
		||||
        } else if (mouse_mode == RED.state.MOVING) {
 | 
			
		||||
@@ -899,6 +1002,10 @@ RED.view = (function() {
 | 
			
		||||
    function canvasMouseUp() {
 | 
			
		||||
        var i;
 | 
			
		||||
        var historyEvent;
 | 
			
		||||
        if (mouse_mode === RED.state.PANNING) {
 | 
			
		||||
            resetMouseVars();
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (mouse_mode === RED.state.QUICK_JOINING) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1013,17 +1120,20 @@ RED.view = (function() {
 | 
			
		||||
    function zoomIn() {
 | 
			
		||||
        if (scaleFactor < 2) {
 | 
			
		||||
            scaleFactor += 0.1;
 | 
			
		||||
            RED.view.navigator.resize();
 | 
			
		||||
            redraw();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function zoomOut() {
 | 
			
		||||
        if (scaleFactor > 0.3) {
 | 
			
		||||
            scaleFactor -= 0.1;
 | 
			
		||||
            RED.view.navigator.resize();
 | 
			
		||||
            redraw();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function zoomZero() {
 | 
			
		||||
        scaleFactor = 1;
 | 
			
		||||
        RED.view.navigator.resize();
 | 
			
		||||
        redraw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1141,7 +1251,6 @@ RED.view = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var selectionJSON = activeWorkspace+":"+JSON.stringify(selection,function(key,value) {
 | 
			
		||||
            if (key === 'nodes') {
 | 
			
		||||
                return value.map(function(n) { return n.id })
 | 
			
		||||
@@ -1348,7 +1457,7 @@ RED.view = (function() {
 | 
			
		||||
        mouseup_node = null;
 | 
			
		||||
        mousedown_link = null;
 | 
			
		||||
        mouse_mode = 0;
 | 
			
		||||
        mousedown_port_type = PORT_TYPE_OUTPUT;
 | 
			
		||||
        mousedown_port_type = null;
 | 
			
		||||
        activeSpliceLink = null;
 | 
			
		||||
        spliceActive = false;
 | 
			
		||||
        d3.select(".link_splice").classed("link_splice",false);
 | 
			
		||||
@@ -1361,6 +1470,9 @@ RED.view = (function() {
 | 
			
		||||
    function disableQuickJoinEventHandler(evt) {
 | 
			
		||||
        // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari)
 | 
			
		||||
        if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91) {
 | 
			
		||||
            if (quickAddActive && drag_lines.length > 0) {
 | 
			
		||||
                quickAddLink = drag_lines[0];
 | 
			
		||||
            }
 | 
			
		||||
            resetMouseVars();
 | 
			
		||||
            hideDragLines();
 | 
			
		||||
            redraw();
 | 
			
		||||
@@ -1372,6 +1484,10 @@ RED.view = (function() {
 | 
			
		||||
        //console.log(d,portType,portIndex);
 | 
			
		||||
        // disable zoom
 | 
			
		||||
        //vis.call(d3.behavior.zoom().on("zoom"), null);
 | 
			
		||||
        if (d3.event.button === 1) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mousedown_node = d;
 | 
			
		||||
        mousedown_port_type = portType;
 | 
			
		||||
        mousedown_port_index = portIndex || 0;
 | 
			
		||||
@@ -1390,7 +1506,7 @@ RED.view = (function() {
 | 
			
		||||
 | 
			
		||||
    function portMouseUp(d,portType,portIndex) {
 | 
			
		||||
        var i;
 | 
			
		||||
        if (mouse_mode === RED.state.QUICK_JOINING) {
 | 
			
		||||
        if (mouse_mode === RED.state.QUICK_JOINING && drag_lines.length > 0) {
 | 
			
		||||
            if (drag_lines[0].node===d) {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
@@ -1603,6 +1719,9 @@ RED.view = (function() {
 | 
			
		||||
 | 
			
		||||
    function nodeMouseDown(d) {
 | 
			
		||||
        focusView();
 | 
			
		||||
        if (d3.event.button === 1) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        //var touch0 = d3.event;
 | 
			
		||||
        //var pos = [touch0.pageX,touch0.pageY];
 | 
			
		||||
        //RED.touch.radialMenu.show(d3.select(this),pos);
 | 
			
		||||
@@ -1646,7 +1765,9 @@ RED.view = (function() {
 | 
			
		||||
        clickElapsed = now-clickTime;
 | 
			
		||||
        clickTime = now;
 | 
			
		||||
 | 
			
		||||
        dblClickPrimed = (lastClickNode == mousedown_node);
 | 
			
		||||
        dblClickPrimed = (lastClickNode == mousedown_node &&
 | 
			
		||||
            d3.event.buttons === 1 &&
 | 
			
		||||
            !d3.event.shiftKey && !d3.event.metaKey && !d3.event.altKey && !d3.event.ctrlKey);
 | 
			
		||||
        lastClickNode = mousedown_node;
 | 
			
		||||
 | 
			
		||||
        var i;
 | 
			
		||||
@@ -1917,7 +2038,7 @@ RED.view = (function() {
 | 
			
		||||
                            .attr("ry",4)
 | 
			
		||||
                            .attr("width",16)
 | 
			
		||||
                            .attr("height",node_height-12)
 | 
			
		||||
                            .attr("fill",function(d) { return d._def.color;})
 | 
			
		||||
                            .attr("fill",function(d) { return RED.utils.getNodeColor(d.type,d._def); /*d._def.color;*/})
 | 
			
		||||
                            .attr("cursor","pointer")
 | 
			
		||||
                            .on("mousedown",function(d) {if (!lasso && isButtonEnabled(d)) {focusView();d3.select(this).attr("fill-opacity",0.2);d3.event.preventDefault(); d3.event.stopPropagation();}})
 | 
			
		||||
                            .on("mouseup",function(d) {if (!lasso && isButtonEnabled(d)) { d3.select(this).attr("fill-opacity",0.4);d3.event.preventDefault();d3.event.stopPropagation();}})
 | 
			
		||||
@@ -1938,7 +2059,7 @@ RED.view = (function() {
 | 
			
		||||
                        .classed("node_unknown",function(d) { return d.type == "unknown"; })
 | 
			
		||||
                        .attr("rx", 5)
 | 
			
		||||
                        .attr("ry", 5)
 | 
			
		||||
                        .attr("fill",function(d) { return d._def.color;})
 | 
			
		||||
                        .attr("fill",function(d) { return RED.utils.getNodeColor(d.type,d._def); /*d._def.color;*/})
 | 
			
		||||
                        .on("mouseup",nodeMouseUp)
 | 
			
		||||
                        .on("mousedown",nodeMouseDown)
 | 
			
		||||
                        .on("touchstart",function(d) {
 | 
			
		||||
@@ -2335,32 +2456,17 @@ RED.view = (function() {
 | 
			
		||||
                        var numOutputs = d.source.outputs || 1;
 | 
			
		||||
                        var sourcePort = d.sourcePort || 0;
 | 
			
		||||
                        var y = -((numOutputs-1)/2)*13 +13*sourcePort;
 | 
			
		||||
 | 
			
		||||
                        var dy = d.target.y-(d.source.y+y);
 | 
			
		||||
                        var dx = (d.target.x-d.target.w/2)-(d.source.x+d.source.w/2);
 | 
			
		||||
                        var delta = Math.sqrt(dy*dy+dx*dx);
 | 
			
		||||
                        var scale = lineCurveScale;
 | 
			
		||||
                        var scaleY = 0;
 | 
			
		||||
                        if (delta < node_width) {
 | 
			
		||||
                            scale = 0.75-0.75*((node_width-delta)/node_width);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (dx < 0) {
 | 
			
		||||
                            scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
 | 
			
		||||
                            if (Math.abs(dy) < 3*node_height) {
 | 
			
		||||
                                scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        d.x1 = d.source.x+d.source.w/2;
 | 
			
		||||
                        d.y1 = d.source.y+y;
 | 
			
		||||
                        d.x2 = d.target.x-d.target.w/2;
 | 
			
		||||
                        d.y2 = d.target.y;
 | 
			
		||||
 | 
			
		||||
                        return "M "+d.x1+" "+d.y1+
 | 
			
		||||
                            " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
 | 
			
		||||
                            (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
 | 
			
		||||
                            d.x2+" "+d.y2;
 | 
			
		||||
                        // return "M "+d.x1+" "+d.y1+
 | 
			
		||||
                        //     " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
 | 
			
		||||
                        //     (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
 | 
			
		||||
                        //     d.x2+" "+d.y2;
 | 
			
		||||
 | 
			
		||||
                        return generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
@@ -2496,7 +2602,7 @@ RED.view = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            ).classed("link_selected", false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RED.view.navigator.refresh();
 | 
			
		||||
        if (d3.event) {
 | 
			
		||||
            d3.event.preventDefault();
 | 
			
		||||
        }
 | 
			
		||||
@@ -2581,7 +2687,7 @@ RED.view = (function() {
 | 
			
		||||
                            try {
 | 
			
		||||
                                node.n._def.onadd.call(node.n);
 | 
			
		||||
                            } catch(err) {
 | 
			
		||||
                                console.log("onadd:",err);
 | 
			
		||||
                                console.log("Definition error: "+node.n.type+".onadd:",err);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
@@ -2767,10 +2873,12 @@ RED.view = (function() {
 | 
			
		||||
            if (v === undefined) {
 | 
			
		||||
                return gridSize;
 | 
			
		||||
            } else {
 | 
			
		||||
                gridSize = v;
 | 
			
		||||
                gridSize = Math.max(5,v);
 | 
			
		||||
                updateGrid();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getActiveNodes: function() {
 | 
			
		||||
            return activeNodes;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ RED.workspaces = (function() {
 | 
			
		||||
                workspaceIndex += 1;
 | 
			
		||||
            } while ($("#workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
 | 
			
		||||
 | 
			
		||||
            ws = {type:"tab",id:tabId,label:RED._('workspace.defaultName',{number:workspaceIndex})};
 | 
			
		||||
            ws = {type:"tab",id:tabId,disabled: false,info:"",label:RED._('workspace.defaultName',{number:workspaceIndex})};
 | 
			
		||||
            RED.nodes.addWorkspace(ws);
 | 
			
		||||
            workspace_tabs.addTab(ws);
 | 
			
		||||
            workspace_tabs.activateTab(tabId);
 | 
			
		||||
@@ -43,7 +43,7 @@ RED.workspaces = (function() {
 | 
			
		||||
        return ws;
 | 
			
		||||
    }
 | 
			
		||||
    function deleteWorkspace(ws) {
 | 
			
		||||
        if (workspace_tabs.count() == 1) {
 | 
			
		||||
        if (workspaceTabCount === 1) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        removeWorkspace(ws);
 | 
			
		||||
@@ -65,7 +65,7 @@ RED.workspaces = (function() {
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    id: "node-dialog-delete",
 | 
			
		||||
                    class: 'leftButton'+((workspace_tabs.count() == 1)?" disabled":""),
 | 
			
		||||
                    class: 'leftButton'+((workspaceTabCount === 1)?" disabled":""),
 | 
			
		||||
                    text: RED._("common.label.delete"), //'<i class="fa fa-trash"></i>',
 | 
			
		||||
                    click: function() {
 | 
			
		||||
                        deleteWorkspace(workspace);
 | 
			
		||||
@@ -172,12 +172,12 @@ RED.workspaces = (function() {
 | 
			
		||||
                        i.addClass('fa-toggle-on');
 | 
			
		||||
                        i.removeClass('fa-toggle-off');
 | 
			
		||||
                        $("#node-input-disabled").prop("checked",false);
 | 
			
		||||
                        $("#node-input-disabled-label").html(RED._("editor:workspace.enabled"));
 | 
			
		||||
                        $("#node-input-disabled-label").text(RED._("editor:workspace.enabled"));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        i.addClass('fa-toggle-off');
 | 
			
		||||
                        i.removeClass('fa-toggle-on');
 | 
			
		||||
                        $("#node-input-disabled").prop("checked",true);
 | 
			
		||||
                        $("#node-input-disabled-label").html(RED._("editor:workspace.disabled"));
 | 
			
		||||
                        $("#node-input-disabled-label").text(RED._("editor:workspace.disabled"));
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
@@ -185,13 +185,13 @@ RED.workspaces = (function() {
 | 
			
		||||
                    $("#node-input-disabled").prop("checked",workspace.disabled);
 | 
			
		||||
                    if (workspace.disabled) {
 | 
			
		||||
                        dialogForm.find("#node-input-disabled-btn i").removeClass('fa-toggle-on').addClass('fa-toggle-off');
 | 
			
		||||
                        $("#node-input-disabled-label").html(RED._("editor:workspace.disabled"));
 | 
			
		||||
                        $("#node-input-disabled-label").text(RED._("editor:workspace.disabled"));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $("#node-input-disabled-label").html(RED._("editor:workspace.enabled"));
 | 
			
		||||
                        $("#node-input-disabled-label").text(RED._("editor:workspace.enabled"));
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    workspace.disabled = false;
 | 
			
		||||
                    $("#node-input-disabled-label").html(RED._("editor:workspace.enabled"));
 | 
			
		||||
                    $("#node-input-disabled-label").text(RED._("editor:workspace.enabled"));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $('<input type="text" style="display: none;" />').prependTo(dialogForm);
 | 
			
		||||
@@ -214,6 +214,7 @@ RED.workspaces = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var workspace_tabs;
 | 
			
		||||
    var workspaceTabCount = 0;
 | 
			
		||||
    function createWorkspaceTabs() {
 | 
			
		||||
        workspace_tabs = RED.tabs.create({
 | 
			
		||||
            id: "workspace-tabs",
 | 
			
		||||
@@ -240,14 +241,26 @@ RED.workspaces = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onadd: function(tab) {
 | 
			
		||||
                if (tab.type === "tab") {
 | 
			
		||||
                    workspaceTabCount++;
 | 
			
		||||
                }
 | 
			
		||||
                $('<span class="workspace-disabled-icon"><i class="fa fa-ban"></i> </span>').prependTo("#red-ui-tab-"+(tab.id.replace(".","-"))+" .red-ui-tab-label");
 | 
			
		||||
                if (tab.disabled) {
 | 
			
		||||
                    $("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('workspace-disabled');
 | 
			
		||||
                }
 | 
			
		||||
                RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
 | 
			
		||||
                RED.menu.setDisabled("menu-item-workspace-delete",workspaceTabCount <= 1);
 | 
			
		||||
                if (workspaceTabCount === 1) {
 | 
			
		||||
                    showWorkspace();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onremove: function(tab) {
 | 
			
		||||
                RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
 | 
			
		||||
                if (tab.type === "tab") {
 | 
			
		||||
                    workspaceTabCount--;
 | 
			
		||||
                }
 | 
			
		||||
                RED.menu.setDisabled("menu-item-workspace-delete",workspaceTabCount <= 1);
 | 
			
		||||
                if (workspaceTabCount === 0) {
 | 
			
		||||
                    hideWorkspace();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onreorder: function(oldOrder, newOrder) {
 | 
			
		||||
                RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()});
 | 
			
		||||
@@ -260,6 +273,17 @@ RED.workspaces = (function() {
 | 
			
		||||
                addWorkspace();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        workspaceTabCount = 0;
 | 
			
		||||
    }
 | 
			
		||||
    function showWorkspace() {
 | 
			
		||||
        $("#workspace .red-ui-tabs").show()
 | 
			
		||||
        $("#chart").show()
 | 
			
		||||
        $("#workspace-footer").children().show()
 | 
			
		||||
    }
 | 
			
		||||
    function hideWorkspace() {
 | 
			
		||||
        $("#workspace .red-ui-tabs").hide()
 | 
			
		||||
        $("#chart").hide()
 | 
			
		||||
        $("#workspace-footer").children().hide()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function init() {
 | 
			
		||||
@@ -280,6 +304,8 @@ RED.workspaces = (function() {
 | 
			
		||||
        RED.actions.add("core:add-flow",addWorkspace);
 | 
			
		||||
        RED.actions.add("core:edit-flow",editWorkspace);
 | 
			
		||||
        RED.actions.add("core:remove-flow",removeWorkspace);
 | 
			
		||||
 | 
			
		||||
        hideWorkspace();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function editWorkspace(id) {
 | 
			
		||||
@@ -294,6 +320,9 @@ RED.workspaces = (function() {
 | 
			
		||||
                workspace_tabs.removeTab(ws.id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (ws.id === activeWorkspace) {
 | 
			
		||||
            activeWorkspace = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setWorkspaceOrder(order) {
 | 
			
		||||
@@ -313,7 +342,7 @@ RED.workspaces = (function() {
 | 
			
		||||
            return workspace_tabs.contains(id);
 | 
			
		||||
        },
 | 
			
		||||
        count: function() {
 | 
			
		||||
            return workspace_tabs.count();
 | 
			
		||||
            return workspaceTabCount;
 | 
			
		||||
        },
 | 
			
		||||
        active: function() {
 | 
			
		||||
            return activeWorkspace
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ RED.user = (function() {
 | 
			
		||||
                    for (;i<data.prompts.length;i++) {
 | 
			
		||||
                        var field = data.prompts[i];
 | 
			
		||||
                        var row = $("<div/>",{class:"form-row"});
 | 
			
		||||
                        $('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row);
 | 
			
		||||
                        $('<label for="node-dialog-login-'+field.id+'">'+RED._(field.label)+':</label><br/>').appendTo(row);
 | 
			
		||||
                        var input = $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row);
 | 
			
		||||
 | 
			
		||||
                        if (i<data.prompts.length-1) {
 | 
			
		||||
@@ -188,6 +188,7 @@ RED.user = (function() {
 | 
			
		||||
                        RED.settings.load(function() {
 | 
			
		||||
                            RED.notify(RED._("user.loggedInAs",{name:RED.settings.user.username}),"success");
 | 
			
		||||
                            updateUserMenu();
 | 
			
		||||
                            RED.events.emit("login",RED.settings.user.username);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
@@ -230,10 +231,66 @@ RED.user = (function() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var readRE = /^((.+)\.)?read$/
 | 
			
		||||
    var writeRE = /^((.+)\.)?write$/
 | 
			
		||||
 | 
			
		||||
    function hasPermission(permission) {
 | 
			
		||||
        if (permission === "") {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (!RED.settings.user) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return checkPermission(RED.settings.user.permissions||"",permission);
 | 
			
		||||
    }
 | 
			
		||||
    function checkPermission(userScope,permission) {
 | 
			
		||||
        if (permission === "") {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        var i;
 | 
			
		||||
 | 
			
		||||
        if (Array.isArray(permission)) {
 | 
			
		||||
            // Multiple permissions requested - check each one
 | 
			
		||||
            for (i=0;i<permission.length;i++) {
 | 
			
		||||
                if (!checkPermission(userScope,permission[i])) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // All permissions check out
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Array.isArray(userScope)) {
 | 
			
		||||
            if (userScope.length === 0) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            for (i=0;i<userScope.length;i++) {
 | 
			
		||||
                if (checkPermission(userScope[i],permission)) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (userScope === "*" || userScope === permission) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (userScope === "read" || userScope === "*.read") {
 | 
			
		||||
            return readRE.test(permission);
 | 
			
		||||
        } else if (userScope === "write" || userScope === "*.write") {
 | 
			
		||||
            return writeRE.test(permission);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: init,
 | 
			
		||||
        login: login,
 | 
			
		||||
        logout: logout
 | 
			
		||||
        logout: logout,
 | 
			
		||||
        hasPermission: hasPermission
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								editor/sass/bootstrap.scss
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								editor/sass/bootstrap.scss
									
									
									
									
										vendored
									
									
								
							@@ -19,3 +19,7 @@
 | 
			
		||||
div.btn-group, a.btn {
 | 
			
		||||
   @include disable-selection;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dropdown-menu>li>a {
 | 
			
		||||
    color: #444;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -66,4 +66,4 @@ $editor-button-background-primary-hover: #6E0A1E;
 | 
			
		||||
$editor-button-color: #999;
 | 
			
		||||
$editor-button-background: #fff;
 | 
			
		||||
 | 
			
		||||
$shade-color: rgba(200,200,200,0.5);
 | 
			
		||||
$shade-color: rgba(160,160,160,0.5);
 | 
			
		||||
 
 | 
			
		||||
@@ -64,22 +64,22 @@
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .debug-message-row {
 | 
			
		||||
        .debug-message-tools-pin {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        &.debug-message-row-pinned .debug-message-tools-pin {
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
        }
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            &>.debug-message-tools {
 | 
			
		||||
                .debug-message-tools-copy {
 | 
			
		||||
                    display: inline-block;
 | 
			
		||||
                }
 | 
			
		||||
                .debug-message-tools-pin {
 | 
			
		||||
                    display: inline-block;
 | 
			
		||||
                }
 | 
			
		||||
}
 | 
			
		||||
.debug-message-row {
 | 
			
		||||
    .debug-message-tools-pin {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
    &.debug-message-row-pinned .debug-message-tools-pin {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background: #f3f3f3;
 | 
			
		||||
        &>.debug-message-tools {
 | 
			
		||||
            .debug-message-tools-copy {
 | 
			
		||||
                display: inline-block;
 | 
			
		||||
            }
 | 
			
		||||
            .debug-message-tools-pin {
 | 
			
		||||
                display: inline-block;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -113,8 +113,8 @@
 | 
			
		||||
 | 
			
		||||
.debug-message-meta {
 | 
			
		||||
    background: #fff;
 | 
			
		||||
    font-size: 10px;
 | 
			
		||||
    color: #777;
 | 
			
		||||
    font-size: 11px;
 | 
			
		||||
    color: #707070;
 | 
			
		||||
}
 | 
			
		||||
.debug-message-date {
 | 
			
		||||
    padding: 1px 5px 1px 1px;
 | 
			
		||||
@@ -125,7 +125,7 @@
 | 
			
		||||
}
 | 
			
		||||
.debug-message-name {
 | 
			
		||||
    padding: 1px 5px;
 | 
			
		||||
    color: #777;
 | 
			
		||||
    color: #707070;
 | 
			
		||||
}
 | 
			
		||||
.debug-message-tools {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -159,7 +159,7 @@
 | 
			
		||||
.debug-message-element {
 | 
			
		||||
    color: #333;
 | 
			
		||||
    font-family: Menlo, monospace;
 | 
			
		||||
    font-size: 12px !important;
 | 
			
		||||
    font-size: 13px !important;
 | 
			
		||||
    line-height: 1.3em;
 | 
			
		||||
}
 | 
			
		||||
.debug-message-object-key {
 | 
			
		||||
@@ -188,11 +188,9 @@
 | 
			
		||||
.debug-message-element.collapsed>span>.debug-message-object-handle {
 | 
			
		||||
    transform: rotate(0deg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.debug-message-object-entry.collapsed > .debug-message-object-entry {
 | 
			
		||||
    display:none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.debug-message-element.collapsed .debug-message-object-entry {
 | 
			
		||||
    display:none;
 | 
			
		||||
}
 | 
			
		||||
@@ -202,14 +200,13 @@
 | 
			
		||||
.debug-message-element.collapsed .debug-message-buffer-opts {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.debug-message-element.collapsed .debug-message-object-type-header {
 | 
			
		||||
    display:none;
 | 
			
		||||
}
 | 
			
		||||
.debug-message-object-entry pre {
 | 
			
		||||
    font-family: Menlo, monospace;
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    line-height: 1.4em;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
    line-height: 1.2em;
 | 
			
		||||
    margin: 0 0 0 -1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,57 +15,65 @@
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#node-dialog-view-diff {
 | 
			
		||||
    height: 600px;
 | 
			
		||||
.node-dialog-view-diff-panel {
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
    padding-top: 30px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        border-radius:1px;
 | 
			
		||||
        padding:0;
 | 
			
		||||
        background: #f9f9f9;
 | 
			
		||||
    }
 | 
			
		||||
    #node-dialog-view-diff-diff {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top:80px;
 | 
			
		||||
        bottom:10px;
 | 
			
		||||
        left:10px;
 | 
			
		||||
        right:10px;
 | 
			
		||||
    .node-dialog-view-diff-diff {
 | 
			
		||||
        li {
 | 
			
		||||
            background: #f9f9f9;
 | 
			
		||||
            padding: 0px;
 | 
			
		||||
            border: none;
 | 
			
		||||
            min-height: 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList-item-content {
 | 
			
		||||
        padding: 5px;
 | 
			
		||||
        // padding-bottom: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#node-dialog-view-diff-headers {
 | 
			
		||||
.node-diff-container {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left:17px;
 | 
			
		||||
    right:32px;
 | 
			
		||||
    top: 55px;
 | 
			
		||||
    top: 40px;
 | 
			
		||||
    right:0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-dialog-view-diff-headers {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left:232px;
 | 
			
		||||
    right:12px;
 | 
			
		||||
    top: 5px;
 | 
			
		||||
    height: 25px;
 | 
			
		||||
    .node-diff-node-entry-cell:not(:first-child) {
 | 
			
		||||
    div {
 | 
			
		||||
        height: 25px;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        box-sizing: border-box;
 | 
			
		||||
        padding-top: 2px;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
        text-overflow: ellipsis;
 | 
			
		||||
        width: 50%;
 | 
			
		||||
        background: #f9f9f9;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        border-top: 1px solid $secondary-border-color;
 | 
			
		||||
        border-color:$secondary-border-color;
 | 
			
		||||
        border-left: 1px solid $secondary-border-color;
 | 
			
		||||
    }
 | 
			
		||||
    .node-diff-node-entry-cell:last-child {
 | 
			
		||||
    div:last-child {
 | 
			
		||||
        border-right: 1px solid $secondary-border-color;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-diff-toolbar {
 | 
			
		||||
    position:absolute;
 | 
			
		||||
    top:0;
 | 
			
		||||
    left:0;
 | 
			
		||||
    right:0;
 | 
			
		||||
    height: 43px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    color: #666;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
@@ -106,10 +114,10 @@
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
 | 
			
		||||
    &:first-child {
 | 
			
		||||
        border-top: 1px solid #eee;
 | 
			
		||||
        border-top: 1px solid $secondary-border-color;
 | 
			
		||||
    }
 | 
			
		||||
    &:not(:last-child) {
 | 
			
		||||
        border-bottom: 1px solid #eee;
 | 
			
		||||
        border-bottom: 1px solid $secondary-border-color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.collapsed {
 | 
			
		||||
@@ -132,14 +140,21 @@
 | 
			
		||||
    table {
 | 
			
		||||
        border-collapse: collapse;
 | 
			
		||||
        table-layout:fixed;
 | 
			
		||||
        
 | 
			
		||||
        // Fix for table-layout: fixed on safari:
 | 
			
		||||
        max-width: none;
 | 
			
		||||
        width: auto;
 | 
			
		||||
        min-width: 100%;
 | 
			
		||||
        width: calc(100% - 20px);
 | 
			
		||||
        margin-left: 20px;
 | 
			
		||||
    }
 | 
			
		||||
    col:first-child {
 | 
			
		||||
        width: 180px;
 | 
			
		||||
    }
 | 
			
		||||
    col:not(:first-child) {
 | 
			
		||||
        width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
    td, th {
 | 
			
		||||
        border: 1px solid $secondary-border-color;
 | 
			
		||||
        border-top: 1px solid #f3f3f3;
 | 
			
		||||
        border-left: 1px solid $secondary-border-color;
 | 
			
		||||
        &:first-child {
 | 
			
		||||
            border-left: none;
 | 
			
		||||
        }
 | 
			
		||||
        padding: 0 0 0 3px;
 | 
			
		||||
        text-align: left;
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
@@ -150,13 +165,12 @@
 | 
			
		||||
            white-space:nowrap;
 | 
			
		||||
            overflow:hidden;
 | 
			
		||||
        }
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f9f9f9;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    td:first-child {
 | 
			
		||||
        width: 140px;
 | 
			
		||||
    }
 | 
			
		||||
    td:not(:first-child) {
 | 
			
		||||
        width: calc( 100% - 140px);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    td  {
 | 
			
		||||
        .node-diff-status {
 | 
			
		||||
            margin-left: 0;
 | 
			
		||||
@@ -179,8 +193,8 @@
 | 
			
		||||
            width: 220px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    td:not(:first-child) {
 | 
			
		||||
        width: calc( (100% - 140px) / 2);
 | 
			
		||||
    col:not(:first-child) {
 | 
			
		||||
        width:50%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .node-diff-node-entry {
 | 
			
		||||
@@ -210,6 +224,9 @@
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    // background: #f6f6f6;
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background: #f9f9f9;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.node-diff-tab-title-meta {
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
@@ -218,6 +235,9 @@
 | 
			
		||||
}
 | 
			
		||||
.node-diff-node-entry-header {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background: #f9f9f9;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.node-diff-node-entry-node {
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
@@ -247,6 +267,9 @@
 | 
			
		||||
    }
 | 
			
		||||
    .node-diff-tab-title {
 | 
			
		||||
        cursor: default;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.node-diff-node-deleted {
 | 
			
		||||
@@ -301,7 +324,7 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.node-diff-node-entry-properties {
 | 
			
		||||
    margin: 5px ;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    color: #666;
 | 
			
		||||
}
 | 
			
		||||
.node-diff-status {
 | 
			
		||||
@@ -313,6 +336,10 @@
 | 
			
		||||
    margin-bottom: 6px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
.node-diff-element {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    width: calc(100% - 20px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-diff-node-description {
 | 
			
		||||
    color: $form-text-color;
 | 
			
		||||
@@ -337,6 +364,7 @@
 | 
			
		||||
.node-diff-added { color: #009900}
 | 
			
		||||
.node-diff-deleted { color: #f80000}
 | 
			
		||||
.node-diff-changed { color: #f89406}
 | 
			
		||||
.node-diff-unchanged { color: #bbb}
 | 
			
		||||
.node-diff-conflicted { color: purple}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -346,7 +374,7 @@
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    width: calc( (100% - 20px) / 2);
 | 
			
		||||
    height: 32px;
 | 
			
		||||
    border-left: 1px solid #eee;
 | 
			
		||||
    border-left: 1px solid $secondary-border-color;
 | 
			
		||||
    padding-top: 2px;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
@@ -480,37 +508,169 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#node-dialog-confirm-deploy {
 | 
			
		||||
    .node-dialog-confirm-row {
 | 
			
		||||
        text-align: left; padding-top: 10px;
 | 
			
		||||
ul.node-dialog-configm-deploy-list {
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
    width: 400px;
 | 
			
		||||
    margin: 10px auto;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-dialog-confirm-conflict-row {
 | 
			
		||||
    img {
 | 
			
		||||
        vertical-align:middle;
 | 
			
		||||
        height: 30px;
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
    ul {
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        width: 400px;
 | 
			
		||||
        margin: 10px auto;
 | 
			
		||||
        text-align: left;
 | 
			
		||||
    i {
 | 
			
		||||
        vertical-align:middle;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        font-size: 30px;
 | 
			
		||||
        width: 30px;
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
    .node-dialog-confirm-conflict-row {
 | 
			
		||||
        img {
 | 
			
		||||
            vertical-align:middle;
 | 
			
		||||
            height: 30px;
 | 
			
		||||
            margin-right: 10px;
 | 
			
		||||
        }
 | 
			
		||||
        i {
 | 
			
		||||
             vertical-align:middle;
 | 
			
		||||
             text-align: center;
 | 
			
		||||
             font-size: 30px;
 | 
			
		||||
             width: 30px;
 | 
			
		||||
             margin-right: 10px;
 | 
			
		||||
        }
 | 
			
		||||
        div {
 | 
			
		||||
            vertical-align: middle;
 | 
			
		||||
            width: calc(100% - 60px);
 | 
			
		||||
            display:inline-block;
 | 
			
		||||
        }
 | 
			
		||||
    div {
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        width: calc(100% - 60px);
 | 
			
		||||
        display:inline-block;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#node-diff-toolbar-resolved-conflicts .node-diff-status {
 | 
			
		||||
    margin:0;
 | 
			
		||||
}
 | 
			
		||||
.node-diff-text-diff-button {
 | 
			
		||||
    float: right;
 | 
			
		||||
    margin: 2px 3px;
 | 
			
		||||
    line-height: 14px;
 | 
			
		||||
    height: 16px;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.node-text-diff {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow-y:auto;
 | 
			
		||||
 | 
			
		||||
    table.node-text-diff-content {
 | 
			
		||||
        margin: 10px;
 | 
			
		||||
        border: 1px solid $secondary-border-color;
 | 
			
		||||
        border-radius: 3px;
 | 
			
		||||
        table-layout: fixed;
 | 
			
		||||
        width: calc(100% - 20px);
 | 
			
		||||
        td {
 | 
			
		||||
            vertical-align: top;
 | 
			
		||||
            word-wrap: break-word;
 | 
			
		||||
        }
 | 
			
		||||
        td.lineno {
 | 
			
		||||
            font-family: monospace;
 | 
			
		||||
            text-align: right;
 | 
			
		||||
            color: #aaa;
 | 
			
		||||
            background: #f6f6f6;
 | 
			
		||||
            padding: 1px 5px;
 | 
			
		||||
        }
 | 
			
		||||
        td.lineno:nth-child(3) {
 | 
			
		||||
            border-left: 1px solid $secondary-border-color;
 | 
			
		||||
        }
 | 
			
		||||
        td.linetext {
 | 
			
		||||
            font-family: monospace;
 | 
			
		||||
            white-space: pre-wrap;
 | 
			
		||||
            padding: 1px 5px;
 | 
			
		||||
            span.prefix {
 | 
			
		||||
                width: 30px;
 | 
			
		||||
                display: inline-block;
 | 
			
		||||
                text-align: center;
 | 
			
		||||
                color: #999;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        td.blank {
 | 
			
		||||
            background: #f6f6f6;
 | 
			
		||||
        }
 | 
			
		||||
        td.added {
 | 
			
		||||
            background: #eefaee;
 | 
			
		||||
        }
 | 
			
		||||
        td.removed {
 | 
			
		||||
            background: #fadddd;
 | 
			
		||||
        }
 | 
			
		||||
        tr.mergeHeader td {
 | 
			
		||||
            color: #800080;
 | 
			
		||||
            background: #e5f9ff;
 | 
			
		||||
            height: 26px;
 | 
			
		||||
            vertical-align: middle;
 | 
			
		||||
        }
 | 
			
		||||
        tr.mergeHeader-separator td {
 | 
			
		||||
            color: #800080;
 | 
			
		||||
            background: darken(#e5f9ff, 10%);
 | 
			
		||||
            height: 0px;
 | 
			
		||||
        }
 | 
			
		||||
        tr.mergeHeader-ours td {
 | 
			
		||||
            border-top: 2px solid darken(#e5f9ff, 10%);
 | 
			
		||||
        }
 | 
			
		||||
        tr.mergeHeader-theirs td {
 | 
			
		||||
            border-bottom: 2px solid darken(#e5f9ff, 10%);
 | 
			
		||||
        }
 | 
			
		||||
        td.unchanged {
 | 
			
		||||
            color: #999;
 | 
			
		||||
        }
 | 
			
		||||
        tr.unchanged {
 | 
			
		||||
            background: #fefefe;
 | 
			
		||||
        }
 | 
			
		||||
        tr.start-block {
 | 
			
		||||
            border-top: 1px solid #f0f0f0;
 | 
			
		||||
        }
 | 
			
		||||
        tr.end-block {
 | 
			
		||||
            border-bottom: 1px solid #f0f0f0;
 | 
			
		||||
        }
 | 
			
		||||
        tr.node-text-diff-file-header td {
 | 
			
		||||
            .filename {
 | 
			
		||||
                font-family: monospace;
 | 
			
		||||
            }
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            padding: 5px 10px 5px 0;
 | 
			
		||||
            color: #333;
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            i.node-diff-chevron {
 | 
			
		||||
                width: 30px;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        tr.node-text-diff-file-header.collapsed {
 | 
			
		||||
            td i.node-diff-chevron {
 | 
			
		||||
                transform: rotate(-90deg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        tr.node-text-diff-commit-header td {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            padding: 5px 10px;
 | 
			
		||||
            color: #333;
 | 
			
		||||
            h3 {
 | 
			
		||||
                font-size: 1.4em;
 | 
			
		||||
                margin: 0;
 | 
			
		||||
            }
 | 
			
		||||
            .commit-summary {
 | 
			
		||||
                border-top: 1px solid $secondary-border-color;
 | 
			
		||||
                padding-top: 5px;
 | 
			
		||||
                color: #999;
 | 
			
		||||
            }
 | 
			
		||||
            .commit-body {
 | 
			
		||||
                margin-bottom:15px;
 | 
			
		||||
                white-space: pre;
 | 
			
		||||
                line-height: 1.2em;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tr.node-text-diff-header > td:not(.flow-diff) {
 | 
			
		||||
            font-family: monospace;
 | 
			
		||||
            padding: 5px 10px;
 | 
			
		||||
            text-align: left;
 | 
			
		||||
            color: #666;
 | 
			
		||||
            background: #ffd;
 | 
			
		||||
            height: 30px;
 | 
			
		||||
            vertical-align: middle;
 | 
			
		||||
            border-top: 1px solid #f0f0f0;
 | 
			
		||||
            border-bottom: 1px solid #f0f0f0;
 | 
			
		||||
        }
 | 
			
		||||
        tr.node-text-diff-expand td {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            &:hover {
 | 
			
		||||
                background: #ffc;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -132,6 +132,11 @@
 | 
			
		||||
    cursor: col-resize;
 | 
			
		||||
    border-left: 1px solid $primary-border-color;
 | 
			
		||||
    box-shadow: -1px 0 6px rgba(0,0,0,0.1);
 | 
			
		||||
 | 
			
		||||
    &.editor-tray-resize-maximised {
 | 
			
		||||
        background: $background-color;
 | 
			
		||||
        cursor: default;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.editor-tray-resize-button {
 | 
			
		||||
    @include workspace-button;
 | 
			
		||||
@@ -153,7 +158,10 @@
 | 
			
		||||
    top: -1px;
 | 
			
		||||
    bottom: -1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#full-shade {
 | 
			
		||||
    @include shade;
 | 
			
		||||
    z-index: 15;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dialog-form,#dialog-form, #node-config-dialog-edit-form {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
@@ -191,12 +199,16 @@
 | 
			
		||||
    text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-warning {
 | 
			
		||||
    border-color: #d6615f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-text-editor {
 | 
			
		||||
    border:1px solid #ccc;
 | 
			
		||||
    border-radius:5px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    font-size: 14px !important;
 | 
			
		||||
    font-family: monospace !important;
 | 
			
		||||
    font-family: Menlo, Consolas, 'DejaVu Sans Mono', Courier, monospace !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-button {
 | 
			
		||||
@@ -204,8 +216,10 @@
 | 
			
		||||
    height: 34px;
 | 
			
		||||
    line-height: 32px;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    padding: 0 10px;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    &.toggle {
 | 
			
		||||
        @include workspace-button-toggle;
 | 
			
		||||
    }
 | 
			
		||||
@@ -214,6 +228,7 @@
 | 
			
		||||
 | 
			
		||||
.editor-button-small {
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    min-width: 20px;
 | 
			
		||||
    line-height: 18px;
 | 
			
		||||
    font-size: 10px;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
@@ -315,6 +330,13 @@
 | 
			
		||||
    input {
 | 
			
		||||
        width: calc(100% - 100px);
 | 
			
		||||
    }
 | 
			
		||||
    #node-settings-icon-module {
 | 
			
		||||
        width: calc(55% - 50px);
 | 
			
		||||
    }
 | 
			
		||||
    #node-settings-icon-file {
 | 
			
		||||
        width: calc(45% - 55px);
 | 
			
		||||
        margin-left: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.node-label-form-none {
 | 
			
		||||
    span {
 | 
			
		||||
@@ -331,3 +353,64 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
#node-settings-icon {
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
    width: calc(100% - 163px);
 | 
			
		||||
}
 | 
			
		||||
.red-ui-icon-picker {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    border: 1px solid $primary-border-color;
 | 
			
		||||
    box-shadow: 0 1px 6px -3px black;
 | 
			
		||||
    background: white;
 | 
			
		||||
    z-Index: 21;
 | 
			
		||||
    display: none;
 | 
			
		||||
    select {
 | 
			
		||||
        box-sizing: border-box;
 | 
			
		||||
        margin: 3px;
 | 
			
		||||
        width: calc(100% - 6px);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-icon-list {
 | 
			
		||||
    width: 308px;
 | 
			
		||||
    height: 200px;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
    line-height: 0px;
 | 
			
		||||
}
 | 
			
		||||
.red-ui-icon-list-icon {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin: 2px;
 | 
			
		||||
    padding: 4px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background: lighten($node-selected-color,20%);
 | 
			
		||||
    }
 | 
			
		||||
    &.selected {
 | 
			
		||||
        background: lighten($node-selected-color,20%);
 | 
			
		||||
        .red-ui-search-result-node {
 | 
			
		||||
            border-color: white;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-icon-list-module {
 | 
			
		||||
    background: $palette-header-background;
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
    padding: 3px;
 | 
			
		||||
    color: #666;
 | 
			
		||||
    clear: both;
 | 
			
		||||
    i {
 | 
			
		||||
        margin-right: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-icon-meta {
 | 
			
		||||
    border-top: 1px solid $secondary-border-color;
 | 
			
		||||
    span {
 | 
			
		||||
        padding: 4px;
 | 
			
		||||
        color: #666;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
    }
 | 
			
		||||
    button {
 | 
			
		||||
        float: right;
 | 
			
		||||
        margin: 2px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,7 @@
 | 
			
		||||
 | 
			
		||||
.link_outline {
 | 
			
		||||
    stroke: #fff;
 | 
			
		||||
    stroke-width: 4;
 | 
			
		||||
    stroke-width: 5;
 | 
			
		||||
    cursor: crosshair;
 | 
			
		||||
    fill: none;
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
 
 | 
			
		||||
@@ -501,6 +501,11 @@ textarea.span1,
 | 
			
		||||
  padding-top: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
label.disabled {
 | 
			
		||||
    color: #bbb !important;
 | 
			
		||||
    cursor: default;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[disabled],
 | 
			
		||||
select[disabled],
 | 
			
		||||
textarea[disabled],
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,7 @@
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    padding: 6px 14px;
 | 
			
		||||
    margin-right: 8px;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    color: $editor-button-color;
 | 
			
		||||
    background: $editor-button-background;
 | 
			
		||||
 | 
			
		||||
@@ -142,3 +143,10 @@
 | 
			
		||||
        outline: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.ui-widget-overlay {
 | 
			
		||||
    @include shade;
 | 
			
		||||
    z-index: 100;
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -48,29 +48,30 @@
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    cursor:pointer;
 | 
			
		||||
 | 
			
		||||
    &.disabled {
 | 
			
		||||
    &.disabled, &:disabled  {
 | 
			
		||||
        cursor: default;
 | 
			
		||||
        color: $workspace-button-color-disabled !important;
 | 
			
		||||
    }
 | 
			
		||||
    &:hover, &:focus {
 | 
			
		||||
        text-decoration: none;
 | 
			
		||||
    }
 | 
			
		||||
    &:not(.disabled):hover {
 | 
			
		||||
    &:not(.disabled):not(:disabled):hover, {
 | 
			
		||||
        color: $workspace-button-color-hover !important;
 | 
			
		||||
        background: $workspace-button-background-hover;
 | 
			
		||||
    }
 | 
			
		||||
    &:not(.disabled):focus {
 | 
			
		||||
    &:not(.disabled):not(:disabled):focus {
 | 
			
		||||
        color: $workspace-button-color-focus !important;
 | 
			
		||||
    }
 | 
			
		||||
    &:not(.disabled):active {
 | 
			
		||||
    &:not(.disabled):not(:disabled):active {
 | 
			
		||||
        color: $workspace-button-color-active !important;
 | 
			
		||||
        background: $workspace-button-background-active;
 | 
			
		||||
        text-decoration: none;
 | 
			
		||||
    }
 | 
			
		||||
    &.selected:not(.disabled) {
 | 
			
		||||
        color: $workspace-button-color-selected !important;
 | 
			
		||||
        background: $workspace-button-background-active;
 | 
			
		||||
    }
 | 
			
		||||
    // &.selected:not(.disabled):not(:disabled) {
 | 
			
		||||
    //     color: $workspace-button-color-selected !important;
 | 
			
		||||
    //     background: $workspace-button-background-active;
 | 
			
		||||
    //     background: #9f9;
 | 
			
		||||
    // }
 | 
			
		||||
    .button-group &:not(:first-child) {
 | 
			
		||||
        border-left: none;
 | 
			
		||||
        border-top-left-radius: 0;
 | 
			
		||||
@@ -94,43 +95,13 @@
 | 
			
		||||
        border-bottom-right-radius: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .button-row &:not(:first-child) {
 | 
			
		||||
        margin-left: 15px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:focus {
 | 
			
		||||
        outline: 1px solid $workspace-button-color-focus-outline;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.button-group-vertical {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
.button-group:not(:last-child) {
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mixin workspace-button-toggle {
 | 
			
		||||
    @include workspace-button;
 | 
			
		||||
    color: $workspace-button-toggle-color !important;
 | 
			
		||||
    background:$workspace-button-background-active;
 | 
			
		||||
    margin-bottom: 1px;
 | 
			
		||||
    &.selected:not(.disabled) {
 | 
			
		||||
        color: $workspace-button-toggle-color-selected !important;
 | 
			
		||||
        background: $workspace-button-background;
 | 
			
		||||
        border-bottom-width: 2px;
 | 
			
		||||
        border-bottom-color: $form-input-border-selected-color;
 | 
			
		||||
        margin-bottom: 0;
 | 
			
		||||
        cursor: default;
 | 
			
		||||
    }
 | 
			
		||||
    &.disabled {
 | 
			
		||||
        color: $workspace-button-toggle-color-disabled !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@mixin editor-button {
 | 
			
		||||
    @include workspace-button;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    padding: 6px 14px;
 | 
			
		||||
    margin-right: 8px;
 | 
			
		||||
    color: $editor-button-color !important;
 | 
			
		||||
    background: $editor-button-background;
 | 
			
		||||
 | 
			
		||||
    &.primary {
 | 
			
		||||
        border-color: $editor-button-background-primary;
 | 
			
		||||
@@ -147,6 +118,60 @@
 | 
			
		||||
            color: $editor-button-color-primary !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.button-group-vertical {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
.button-group:not(:last-child) {
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mixin workspace-button-toggle {
 | 
			
		||||
    @include workspace-button;
 | 
			
		||||
    color: $workspace-button-toggle-color !important;
 | 
			
		||||
    background:$workspace-button-background-active;
 | 
			
		||||
    margin-bottom: 1px;
 | 
			
		||||
 | 
			
		||||
    &.selected:not(.disabled):not(:disabled) {
 | 
			
		||||
        color: $workspace-button-toggle-color-selected !important;
 | 
			
		||||
        background: $workspace-button-background;
 | 
			
		||||
        border-bottom-width: 2px;
 | 
			
		||||
        border-bottom-color: $form-input-border-selected-color;
 | 
			
		||||
        margin-bottom: 0;
 | 
			
		||||
        &:not(.single) {
 | 
			
		||||
            cursor: default;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.disabled,&:disabled {
 | 
			
		||||
        color: $workspace-button-toggle-color-disabled !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@mixin editor-button {
 | 
			
		||||
    @include workspace-button;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    padding: 6px 14px;
 | 
			
		||||
    margin-right: 8px;
 | 
			
		||||
    color: $editor-button-color !important;
 | 
			
		||||
    background: $editor-button-background;
 | 
			
		||||
 | 
			
		||||
    // &.primary {
 | 
			
		||||
    //     border-color: $editor-button-background-primary;
 | 
			
		||||
    //     color: $editor-button-color-primary !important;
 | 
			
		||||
    //     background: $editor-button-background-primary;
 | 
			
		||||
    //     &.disabled, &.ui-state-disabled {
 | 
			
		||||
    //         background: none;
 | 
			
		||||
    //         color: $editor-button-color !important;
 | 
			
		||||
    //         border-color: $form-input-border-color;
 | 
			
		||||
    //     }
 | 
			
		||||
    //     &:not(.disabled):not(.ui-button-disabled):hover {
 | 
			
		||||
    //         border-color: $editor-button-background-primary-hover;
 | 
			
		||||
    //         background: $editor-button-background-primary-hover;
 | 
			
		||||
    //         color: $editor-button-color-primary !important;
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
    &:not(.disabled):hover {
 | 
			
		||||
        //color: $editor-button-color;
 | 
			
		||||
    }
 | 
			
		||||
@@ -181,7 +206,7 @@
 | 
			
		||||
    height: 25px;
 | 
			
		||||
    line-height: 23px;
 | 
			
		||||
    padding: 0 10px;
 | 
			
		||||
 | 
			
		||||
    user-select: none;
 | 
			
		||||
 | 
			
		||||
    .button-group:not(:last-child) {
 | 
			
		||||
        margin-right: 5px;
 | 
			
		||||
@@ -205,6 +230,7 @@
 | 
			
		||||
    font-size: 11px;
 | 
			
		||||
    line-height: 17px;
 | 
			
		||||
    height: 18px;
 | 
			
		||||
    width: 18px;
 | 
			
		||||
    &.text-button {
 | 
			
		||||
        width: auto;
 | 
			
		||||
        padding: 0 5px;
 | 
			
		||||
@@ -224,4 +250,8 @@
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    background: $shade-color;
 | 
			
		||||
    z-index: 5;
 | 
			
		||||
}
 | 
			
		||||
.component-shade {
 | 
			
		||||
    @include shade
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
#notifications {
 | 
			
		||||
    z-index: 10000;
 | 
			
		||||
    z-index: 100;
 | 
			
		||||
    width: 500px;
 | 
			
		||||
    margin-left: -250px;
 | 
			
		||||
    left: 50%;
 | 
			
		||||
@@ -34,6 +34,10 @@
 | 
			
		||||
    border-left-width: 16px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
.notification p:first-child {
 | 
			
		||||
    font-size: 1.1em;
 | 
			
		||||
    font-weight: 400;
 | 
			
		||||
}
 | 
			
		||||
.notification a {
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    &:hover {
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,12 @@
 | 
			
		||||
                .palette-module-version {
 | 
			
		||||
                    color: #aaa;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .palette-module-errors .fa-warning {
 | 
			
		||||
                    opacity: 0.5;
 | 
			
		||||
                }
 | 
			
		||||
                ul.palette-module-error-list li {
 | 
			
		||||
                    color: #aaa;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
@@ -76,48 +81,9 @@
 | 
			
		||||
        border-bottom: 1px solid $primary-border-color;
 | 
			
		||||
        text-align: right;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-button-group {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        a {
 | 
			
		||||
            margin-left: 5px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-shade {
 | 
			
		||||
        @include shade;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        padding-top: 20px;
 | 
			
		||||
    }
 | 
			
		||||
    #palette-module-install-shade {
 | 
			
		||||
        padding-top: 80px;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-shade-status {
 | 
			
		||||
        color: #666;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .palette-module-meta {
 | 
			
		||||
        color: #666;
 | 
			
		||||
        position: relative;
 | 
			
		||||
        &.disabled {
 | 
			
		||||
            color: #ccc;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .fa {
 | 
			
		||||
            width: 15px;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            margin-right: 5px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-name {
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
        @include enable-selection;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-version, .palette-module-updated, .palette-module-link {
 | 
			
		||||
        font-style:italic;
 | 
			
		||||
        font-size: 0.8em;
 | 
			
		||||
        @include enable-selection;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-updated {
 | 
			
		||||
        margin-left: 10px;
 | 
			
		||||
    }
 | 
			
		||||
@@ -224,3 +190,62 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.palette-module-meta {
 | 
			
		||||
    color: #666;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    &.disabled {
 | 
			
		||||
        color: #ccc;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .fa {
 | 
			
		||||
        width: 15px;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        margin-right: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.palette-module-name {
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    @include enable-selection;
 | 
			
		||||
}
 | 
			
		||||
.palette-module-version, .palette-module-updated, .palette-module-link {
 | 
			
		||||
    font-style:italic;
 | 
			
		||||
    font-size: 0.8em;
 | 
			
		||||
    @include enable-selection;
 | 
			
		||||
}
 | 
			
		||||
.palette-module-section {
 | 
			
		||||
    padding:0 !important;
 | 
			
		||||
    background: #f9f9f9 !important;
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
    color: #666;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.palette-module-button-group {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    a {
 | 
			
		||||
        margin-left: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.palette-module-meta .fa-warning {
 | 
			
		||||
    color: #AD1625;
 | 
			
		||||
}
 | 
			
		||||
ul.palette-module-error-list {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    list-style-type: none;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
    li {
 | 
			
		||||
        border: none;
 | 
			
		||||
        background: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.palette-module-shade {
 | 
			
		||||
    @include shade;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    padding-top: 20px;
 | 
			
		||||
}
 | 
			
		||||
#palette-module-install-shade {
 | 
			
		||||
    padding-top: 80px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -90,13 +90,14 @@
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    padding: 9px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    padding-left: 30px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
 | 
			
		||||
    user-select: none;
 | 
			
		||||
}
 | 
			
		||||
.palette-header i {
 | 
			
		||||
    margin: 3px 10px 3px 3px;
 | 
			
		||||
.palette-header > i {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 11px;
 | 
			
		||||
    top: 12px;
 | 
			
		||||
    -webkit-transition: all 0.2s ease-in-out;
 | 
			
		||||
    -moz-transition: all 0.2s ease-in-out;
 | 
			
		||||
    -o-transition: all 0.2s ease-in-out;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-popover:after, .red-ui-popover:before {
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    border: solid transparent;
 | 
			
		||||
    content: " ";
 | 
			
		||||
    height: 0;
 | 
			
		||||
@@ -39,12 +38,18 @@
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
}
 | 
			
		||||
.red-ui-popover.red-ui-popover-right:after, .red-ui-popover.red-ui-popover-right:before {
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    right: 100%;
 | 
			
		||||
}
 | 
			
		||||
.red-ui-popover.red-ui-popover-left:after, .red-ui-popover.red-ui-popover-left:before {
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    left: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-popover.red-ui-popover-bottom:after, .red-ui-popover.red-ui-popover-bottom:before {
 | 
			
		||||
    bottom: 100%;
 | 
			
		||||
    left: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-popover.red-ui-popover-right:after {
 | 
			
		||||
    border-color: rgba(136, 183, 213, 0);
 | 
			
		||||
@@ -72,6 +77,21 @@
 | 
			
		||||
    margin-top: -11px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.red-ui-popover.red-ui-popover-bottom:after {
 | 
			
		||||
    border-color: rgba(136, 183, 213, 0);
 | 
			
		||||
    border-bottom-color: #fff;
 | 
			
		||||
    border-width: 10px;
 | 
			
		||||
    margin-left: -10px;
 | 
			
		||||
}
 | 
			
		||||
.red-ui-popover.red-ui-popover-bottom:before {
 | 
			
		||||
    border-color: rgba(194, 225, 245, 0);
 | 
			
		||||
    border-bottom-color: $primary-border-color;
 | 
			
		||||
    border-width: 11px;
 | 
			
		||||
    margin-left: -11px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.red-ui-popover-size-small {
 | 
			
		||||
    font-size: 11px;
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
@@ -93,4 +113,12 @@
 | 
			
		||||
        border-width: 6px;
 | 
			
		||||
        margin-top: -6px;
 | 
			
		||||
    }
 | 
			
		||||
    &.red-ui-popover-bottom:after {
 | 
			
		||||
        border-width: 5px;
 | 
			
		||||
        margin-left: -5px;
 | 
			
		||||
    }
 | 
			
		||||
    &.red-ui-popover-bottom:before {
 | 
			
		||||
        border-width: 6px;
 | 
			
		||||
        margin-left: -6px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										875
									
								
								editor/sass/projects.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										875
									
								
								editor/sass/projects.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,875 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
#projects-dialog {
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        padding: 0px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
#project-settings-tab-settings {
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-shade {
 | 
			
		||||
    background: #f3f3f3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-edit-form form {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    .form-row {
 | 
			
		||||
        margin-bottom: 15px;
 | 
			
		||||
        label {
 | 
			
		||||
            color: #555;
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            display: block;
 | 
			
		||||
            &.projects-edit-form-inline-label {
 | 
			
		||||
                font-weight: normal;
 | 
			
		||||
                color: inherit;
 | 
			
		||||
                width: auto;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        input[type=text], input[type=password],textarea {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
        input[type=checkbox], input[type=radio] {
 | 
			
		||||
            width: auto;
 | 
			
		||||
            vertical-align: top;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.projects-edit-form-sublabel {
 | 
			
		||||
    color: #999;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
    margin-bottom: -15px;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
.project-settings-tab-pane {
 | 
			
		||||
    & * .projects-edit-form-sublabel {
 | 
			
		||||
        margin-right: 50px;
 | 
			
		||||
        margin-top: -10px;
 | 
			
		||||
        margin-bottom: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-spinner {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 1px;
 | 
			
		||||
    bottom: 1px;
 | 
			
		||||
    left: 1px;
 | 
			
		||||
    right: 1px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    padding: 40px;
 | 
			
		||||
    background: white;
 | 
			
		||||
    &:before {
 | 
			
		||||
      content: '';
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      vertical-align: middle;
 | 
			
		||||
      margin-right: -0.25em;
 | 
			
		||||
    }
 | 
			
		||||
    img {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        width: 80px;
 | 
			
		||||
    }
 | 
			
		||||
    &.projects-dialog-spinner-sidebar {
 | 
			
		||||
        background: white;
 | 
			
		||||
        padding:0;
 | 
			
		||||
        img {
 | 
			
		||||
            width: 40px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.projects-version-control-spinner-sidebar {
 | 
			
		||||
        background: white;
 | 
			
		||||
        padding:0;
 | 
			
		||||
        img {
 | 
			
		||||
            width: 20px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.projects-dialog-spinner-contain {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        img {
 | 
			
		||||
            width: auto;
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            max-height: 50px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-screen-start {
 | 
			
		||||
    .projects-dialog-screen-start-hero {
 | 
			
		||||
        // background: url(https://nodered.org/images/title-wave.png) no-repeat 0% 100% #8f0000;
 | 
			
		||||
        // background-size: contain;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        font-size: 2em;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        min-height: 60px;
 | 
			
		||||
        color: #555;
 | 
			
		||||
        h1 {
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            color: #f0f0f0;
 | 
			
		||||
            font-size: 2em;
 | 
			
		||||
            font-weight: normal;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-screen-start-body {
 | 
			
		||||
        min-height: 400px;
 | 
			
		||||
        line-height: 1.6em;
 | 
			
		||||
        p {
 | 
			
		||||
            font-size: 1.1em;
 | 
			
		||||
            margin-bottom: 20px;
 | 
			
		||||
        }
 | 
			
		||||
        p:first-child {
 | 
			
		||||
            font-weight: 500;
 | 
			
		||||
            font-size: 1.2em;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    button.editor-button {
 | 
			
		||||
        width: calc(50% - 80px);
 | 
			
		||||
        margin: 20px;
 | 
			
		||||
        height: auto;
 | 
			
		||||
        line-height: 2em;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        border-color: #aaa;
 | 
			
		||||
        i {
 | 
			
		||||
            color: #aaa;
 | 
			
		||||
        }
 | 
			
		||||
        &:hover i {
 | 
			
		||||
            color: #999;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .button-group {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-screen-create {
 | 
			
		||||
    min-height: 500px;
 | 
			
		||||
    button.projects-dialog-screen-create-type {
 | 
			
		||||
        height: auto;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
    }
 | 
			
		||||
    .button-group {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-dialog-screen-secret {
 | 
			
		||||
    min-height: auto;
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-project-list-container {
 | 
			
		||||
    border: 1px solid $secondary-border-color;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-project-list-inner-container {
 | 
			
		||||
    height: 300px;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
    position:relative;
 | 
			
		||||
    .red-ui-editableList-border {
 | 
			
		||||
        border: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-project-list {
 | 
			
		||||
    li {
 | 
			
		||||
        padding: 0 !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-project-list-entry {
 | 
			
		||||
    padding: 12px 0;
 | 
			
		||||
    border-left: 3px solid #fff;
 | 
			
		||||
    border-right: 3px solid #fff;
 | 
			
		||||
    &.projects-list-entry-current {
 | 
			
		||||
        &:not(.selectable) {
 | 
			
		||||
            background: #f9f9f9;
 | 
			
		||||
        }
 | 
			
		||||
        i {
 | 
			
		||||
            color: #999;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.selectable {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            // border-left-color: #aaa;
 | 
			
		||||
            // border-right-color: #aaa;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-project-list-entry-icon {
 | 
			
		||||
        i {
 | 
			
		||||
            color: #ccc;
 | 
			
		||||
            font-size: 2em;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.selected {
 | 
			
		||||
        background: #efefef;
 | 
			
		||||
        border-left-color:#999;
 | 
			
		||||
        border-right-color:#999;
 | 
			
		||||
    }
 | 
			
		||||
    span {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        vertical-align:middle;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-project-list-entry-icon {
 | 
			
		||||
         margin: 0 10px 0 5px;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-project-list-entry-name {
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-project-list-entry-current {
 | 
			
		||||
        float: right;
 | 
			
		||||
        margin-right: 20px;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        color: #999;
 | 
			
		||||
        padding-top: 4px;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-project-list-entry-tools {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 16px;
 | 
			
		||||
        right: 30px;
 | 
			
		||||
        display: none;
 | 
			
		||||
        color: #999;
 | 
			
		||||
    }
 | 
			
		||||
    &:hover {
 | 
			
		||||
        .projects-dialog-project-list-entry-tools {
 | 
			
		||||
            display: block;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-screen-create-type.editor-button.toggle.selected:not(.disabled):not(:disabled) {
 | 
			
		||||
    background: #fff !important;
 | 
			
		||||
    color: #666 !important;
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-screen-input-status {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 2px;
 | 
			
		||||
    right: 8px;
 | 
			
		||||
    width: 70px;
 | 
			
		||||
    height: 30px;
 | 
			
		||||
    color: #999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-stack-info {
 | 
			
		||||
    height: 100px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    border-bottom: 1px solid $secondary-border-color;
 | 
			
		||||
    color: #333;
 | 
			
		||||
    i {
 | 
			
		||||
        color: #999;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-stack {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    .palette-category {
 | 
			
		||||
        &:not(.palette-category-expanded) button {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#project-settings-tab-deps {
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList-border {
 | 
			
		||||
        border-radius: 0;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList-item-content {
 | 
			
		||||
        padding: 0px 6px;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-header {
 | 
			
		||||
        padding: 6px 4px;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-button {
 | 
			
		||||
        float: right;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-unused {
 | 
			
		||||
        & > * {
 | 
			
		||||
            color: #bbb;
 | 
			
		||||
        }
 | 
			
		||||
        // border: 1px dashed #bbb;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-unknown {
 | 
			
		||||
        border: 1px dashed #aaa;
 | 
			
		||||
        background: #fafafa;
 | 
			
		||||
    }
 | 
			
		||||
    .palette-module-not-installed {
 | 
			
		||||
        border: 1px dashed #b07575;
 | 
			
		||||
        background: #fee;
 | 
			
		||||
        i.fa-warning {
 | 
			
		||||
            color: #b07575; //#b72828;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.project-settings-tab-pane {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top:0;
 | 
			
		||||
    left:0;
 | 
			
		||||
    right:0;
 | 
			
		||||
    bottom:0;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
    padding: 8px 20px 20px;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control {
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        background: #f9f9f9;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        li {
 | 
			
		||||
            padding:0;
 | 
			
		||||
            background: #fff;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList-border {
 | 
			
		||||
        border: none;
 | 
			
		||||
        border-radius: 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control-change-container {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    height: 50%;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    transition: height 0.2s ease-in-out;
 | 
			
		||||
    &:first-child {
 | 
			
		||||
        // border-bottom: 1px solid $primary-border-color;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-merging {
 | 
			
		||||
    .sidebar-version-control-change-container {
 | 
			
		||||
        height: 33%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-slide-box {
 | 
			
		||||
    position:absolute;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    left:0;
 | 
			
		||||
    right:0;
 | 
			
		||||
    height:0;
 | 
			
		||||
    transition: height 0.2s ease-in-out;
 | 
			
		||||
    background: #f6f6f6;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    &.sidebar-version-control-slide-box-top {
 | 
			
		||||
        z-index: 4;
 | 
			
		||||
        top: 0px;
 | 
			
		||||
        left: auto;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        max-width: 280px;
 | 
			
		||||
        border-left: 1px solid $primary-border-color;
 | 
			
		||||
        border-right: 1px solid $primary-border-color;
 | 
			
		||||
        border-bottom: 1px solid $primary-border-color;
 | 
			
		||||
        box-shadow: 1px 1px 4px rgba(0,0,0,0.2);
 | 
			
		||||
 | 
			
		||||
        color: #666;
 | 
			
		||||
        background: #f6f6f6;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        box-sizing: border-box;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    &.sidebar-version-control-slide-box-bottom {
 | 
			
		||||
        bottom: 0px;
 | 
			
		||||
        border-top: 1px solid $secondary-border-color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    textarea {
 | 
			
		||||
        height: 110px;
 | 
			
		||||
        margin: 10px;
 | 
			
		||||
        width: calc(100% - 20px);
 | 
			
		||||
        box-sizing: border-box;
 | 
			
		||||
        border-radius: 1px;
 | 
			
		||||
        resize: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.projects-branch-list {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    .red-ui-searchBox-container {
 | 
			
		||||
        border-top: 1px solid $secondary-border-color;
 | 
			
		||||
        border-left: 1px solid $secondary-border-color;
 | 
			
		||||
        border-right: 1px solid $secondary-border-color;
 | 
			
		||||
        border-top-left-radius: 2px;
 | 
			
		||||
        border-top-right-radius: 2px;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList {
 | 
			
		||||
        border: 1px solid $secondary-border-color;
 | 
			
		||||
        border-bottom-left-radius: 2px;
 | 
			
		||||
        border-bottom-right-radius: 2px;
 | 
			
		||||
        & > .red-ui-editableList-border {
 | 
			
		||||
            border-radius: 0;
 | 
			
		||||
            border: none;
 | 
			
		||||
        }
 | 
			
		||||
        .red-ui-editableList-container {
 | 
			
		||||
            padding: 0;
 | 
			
		||||
            li {
 | 
			
		||||
                padding: 0;
 | 
			
		||||
                background: #fff;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.uneditable-input .projects-branch-list {
 | 
			
		||||
    .red-ui-editableList {
 | 
			
		||||
        border-left: none;
 | 
			
		||||
        border-bottom: none;
 | 
			
		||||
        border-right: none;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-searchBox-container {
 | 
			
		||||
        border-left: none;
 | 
			
		||||
        border-right: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-slide-box-header {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control-slide-box-toolbar {
 | 
			
		||||
    padding: 0 20px;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-branch-list-entry {
 | 
			
		||||
    padding: 5px 8px;
 | 
			
		||||
    color: #666;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    &.selected {
 | 
			
		||||
        border-left-color:#999;
 | 
			
		||||
        border-right-color:#999;
 | 
			
		||||
    }
 | 
			
		||||
    border-left: 2px solid #fff;
 | 
			
		||||
    border-right: 2px solid #fff;
 | 
			
		||||
    margin: 0 1px;
 | 
			
		||||
    i { width: 16px; text-align: center}
 | 
			
		||||
    &.input-error {
 | 
			
		||||
        cursor: default;
 | 
			
		||||
    }
 | 
			
		||||
    &:not(.input-error):hover {
 | 
			
		||||
        background: #f3f3f3;
 | 
			
		||||
        border-left-color:#999;
 | 
			
		||||
        border-right-color:#999;
 | 
			
		||||
    }
 | 
			
		||||
    span {
 | 
			
		||||
        margin-left: 5px;
 | 
			
		||||
    }
 | 
			
		||||
    span.current {
 | 
			
		||||
        float: right;
 | 
			
		||||
        font-size: 0.8em;
 | 
			
		||||
        color: #999;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control-change-entry {
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    span {
 | 
			
		||||
        margin: 0 6px;
 | 
			
		||||
    }
 | 
			
		||||
    a {
 | 
			
		||||
        color: currentColor;
 | 
			
		||||
        &.disabled {
 | 
			
		||||
            pointer-events: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .sidebar-version-control-change-entry-tools {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 4px;
 | 
			
		||||
        right: 4px;
 | 
			
		||||
        display: none;
 | 
			
		||||
        button {
 | 
			
		||||
            width: 24px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:hover {
 | 
			
		||||
        .sidebar-version-control-change-entry-tools {
 | 
			
		||||
            display: block;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.node-info-none {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        background: #fefefe;
 | 
			
		||||
        white-space: normal;
 | 
			
		||||
        height: auto;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control-commit-entry {
 | 
			
		||||
    min-height: 20px;
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background: #eee;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-more {
 | 
			
		||||
    color: #999;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    font-style: italic;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-sha {
 | 
			
		||||
    float: right;
 | 
			
		||||
    font-family: monospace;
 | 
			
		||||
    color: #c38888;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    font-size: 0.85em;
 | 
			
		||||
    margin-left: 5px;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-subject {
 | 
			
		||||
    color: #666;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-refs {
 | 
			
		||||
    min-height: 22px;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-ref {
 | 
			
		||||
    color: #aaa;
 | 
			
		||||
    font-size: 0.7em;
 | 
			
		||||
    border: 1px solid #ccc;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    padding: 2px 5px;
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-date {
 | 
			
		||||
    color: #999;
 | 
			
		||||
    font-size: 0.85em;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-user {
 | 
			
		||||
    float: right;
 | 
			
		||||
    color: #999;
 | 
			
		||||
    font-size: 0.85em;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-commit-head {
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-change-header {
 | 
			
		||||
    color: #666;
 | 
			
		||||
    background: #f6f6f6;
 | 
			
		||||
    padding: 4px 10px;
 | 
			
		||||
    height: 30px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    border-top: 1px solid $secondary-border-color;
 | 
			
		||||
    border-bottom: 1px solid $secondary-border-color;
 | 
			
		||||
    i {
 | 
			
		||||
        transition: all 0.2s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-repo-toolbar {
 | 
			
		||||
    color: #666;
 | 
			
		||||
    background: #f6f6f6;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-version-control-repo-count {
 | 
			
		||||
    margin-right: 8px;
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-repo-action  {
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-version-control-repo-sub-action  {
 | 
			
		||||
    width: calc(50% - 5px);
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
    &:not(:first-child) {
 | 
			
		||||
        margin-right: 0;
 | 
			
		||||
        margin-left: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.project-file-listing-container > .red-ui-editableList > .red-ui-editableList-border {
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
    border: none;
 | 
			
		||||
    border-top: 1px solid $secondary-border-color;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.red-ui-editableList-container .projects-dialog-file-list {
 | 
			
		||||
    .red-ui-editableList-border {
 | 
			
		||||
        border: none;
 | 
			
		||||
    }
 | 
			
		||||
    li {
 | 
			
		||||
        padding: 0 !important;
 | 
			
		||||
        border: none;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-file-list-entry {
 | 
			
		||||
    padding: 3px 0;
 | 
			
		||||
    border-left: 2px solid #fff;
 | 
			
		||||
    border-right: 2px solid #fff;
 | 
			
		||||
    &.projects-list-entry-current {
 | 
			
		||||
        &:not(.selectable) {
 | 
			
		||||
            background: #f9f9f9;
 | 
			
		||||
        }
 | 
			
		||||
        i {
 | 
			
		||||
            color: #999;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.selectable {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            border-left-color:#999;
 | 
			
		||||
            border-right-color:#999;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.unselectable {
 | 
			
		||||
        color: #ccc;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i {
 | 
			
		||||
        color: #999;
 | 
			
		||||
        width: 16px;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
    }
 | 
			
		||||
    &.selected {
 | 
			
		||||
        background: #efefef;
 | 
			
		||||
        border-left-color:#999;
 | 
			
		||||
        border-right-color:#999;
 | 
			
		||||
    }
 | 
			
		||||
    span {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        vertical-align:middle;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-file-list-entry-folder {
 | 
			
		||||
         margin: 0 10px 0 0px;
 | 
			
		||||
 | 
			
		||||
         .fa-angle-right {
 | 
			
		||||
             color: #333;
 | 
			
		||||
             transition: all 0.2s ease-in-out;
 | 
			
		||||
 | 
			
		||||
         }
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-file-list-entry-file {
 | 
			
		||||
         margin: 0 10px 0 20px;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-file-list-entry-name {
 | 
			
		||||
        font-size: 1em;
 | 
			
		||||
    }
 | 
			
		||||
    &.expanded .fa-angle-right {
 | 
			
		||||
        transform: rotate(90deg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-file-list-entry-file-type-git { color: #999 }
 | 
			
		||||
 | 
			
		||||
.projects-dialog-remote-list {
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        li {
 | 
			
		||||
            padding: 0;
 | 
			
		||||
            border: none;
 | 
			
		||||
            border-radius: 4px;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-dialog-sshkey-list {
 | 
			
		||||
    li {
 | 
			
		||||
        padding: 0 !important;
 | 
			
		||||
    }
 | 
			
		||||
    &.projects-dialog-sshkey-list-small {
 | 
			
		||||
        .projects-dialog-sshkey-list-entry {
 | 
			
		||||
            padding: 6px 0;
 | 
			
		||||
            i {
 | 
			
		||||
                font-size: 1em;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .projects-dialog-sshkey-list-entry-name {
 | 
			
		||||
            font-size: 1em;
 | 
			
		||||
        }
 | 
			
		||||
        .projects-dialog-sshkey-list-entry-current {
 | 
			
		||||
            margin-right: 10px;
 | 
			
		||||
            padding-top: 2px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-editableList-container {
 | 
			
		||||
    .projects-dialog-sshkey-list {
 | 
			
		||||
        li:last-child {
 | 
			
		||||
            border-bottom: 0px none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-sshkey-list-entry {
 | 
			
		||||
    padding: 12px 0;
 | 
			
		||||
    border-left: 3px solid #fff;
 | 
			
		||||
    border-right: 3px solid #fff;
 | 
			
		||||
    &.sshkey-list-entry-current {
 | 
			
		||||
        &:not(.selectable) {
 | 
			
		||||
            background: #f9f9f9;
 | 
			
		||||
        }
 | 
			
		||||
        i {
 | 
			
		||||
            color: #999;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &.selectable {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
            border-left-color: #aaa;
 | 
			
		||||
            border-right-color: #aaa;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    i {
 | 
			
		||||
        color: #ccc;
 | 
			
		||||
        font-size: 2em;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    &.selected {
 | 
			
		||||
        background: #efefef;
 | 
			
		||||
        border-left-color:#999;
 | 
			
		||||
        border-right-color:#999;
 | 
			
		||||
    }
 | 
			
		||||
    span {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        vertical-align:middle;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-sshkey-list-entry-icon {
 | 
			
		||||
         margin: 0 10px 0 5px;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-sshkey-list-entry-name {
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-sshkey-list-entry-current {
 | 
			
		||||
        float: right;
 | 
			
		||||
        margin-right: 20px;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        color: #999;
 | 
			
		||||
        padding-top: 4px;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-sshkey-list-button-remove {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        right: 4px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.projects-dialog-ssh-public-key {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    padding: 15px 20px 0;
 | 
			
		||||
    pre {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        word-break: break-all;
 | 
			
		||||
    }
 | 
			
		||||
    &:after {
 | 
			
		||||
        content: "";
 | 
			
		||||
        display: table;
 | 
			
		||||
        clear: both;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-dialog-ssh-key-list {
 | 
			
		||||
    li {
 | 
			
		||||
        padding: 0 !important;
 | 
			
		||||
    }
 | 
			
		||||
    .projects-dialog-ssh-key-header {
 | 
			
		||||
        padding: 10px 5px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: #f3f3f3;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-dialog-list {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    .red-ui-editableList-container {
 | 
			
		||||
        padding: 1px;
 | 
			
		||||
        background: #f6f6f6;
 | 
			
		||||
        li:last-child {
 | 
			
		||||
            border-bottom: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects-dialog-list-entry {
 | 
			
		||||
    &.red-ui-search-empty {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    span {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
    .entry-icon {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        min-width: 30px;
 | 
			
		||||
        vertical-align: top;
 | 
			
		||||
        color: #999;
 | 
			
		||||
    }
 | 
			
		||||
    .entry-name {
 | 
			
		||||
        min-width: 250px;
 | 
			
		||||
    }
 | 
			
		||||
    &.current .entry-name {
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
    }
 | 
			
		||||
    .entry-detail {
 | 
			
		||||
        color: #aaa;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .entry-remote-name {
 | 
			
		||||
        min-width: 250px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .entry-tools {
 | 
			
		||||
        float: right;
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.projects-dialog-list-dialog {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
    margin-bottom: 20px;
 | 
			
		||||
    background: white;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    border: 1px solid $secondary-border-color;
 | 
			
		||||
    .projects-edit-form-sublabel {
 | 
			
		||||
        margin-top: -8px !important;
 | 
			
		||||
        display: block !important;
 | 
			
		||||
        width: auto !important;
 | 
			
		||||
    }
 | 
			
		||||
    &:after {
 | 
			
		||||
        content: "";
 | 
			
		||||
        display: table;
 | 
			
		||||
        clear: both;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .projects-dialog-list-dialog-header {
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
        background: #f3f3f3;
 | 
			
		||||
        margin-top: 0 !important;
 | 
			
		||||
        padding: 5px 10px;
 | 
			
		||||
        margin-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -40,6 +40,7 @@
 | 
			
		||||
@import "panels";
 | 
			
		||||
@import "tabs";
 | 
			
		||||
@import "tab-config";
 | 
			
		||||
@import "tab-context";
 | 
			
		||||
@import "tab-info";
 | 
			
		||||
@import "popover";
 | 
			
		||||
@import "flow";
 | 
			
		||||
@@ -48,12 +49,15 @@
 | 
			
		||||
 | 
			
		||||
@import "userSettings";
 | 
			
		||||
 | 
			
		||||
@import "projects";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@import "ui/common/editableList";
 | 
			
		||||
@import "ui/common/searchBox";
 | 
			
		||||
@import "ui/common/typedInput";
 | 
			
		||||
@import "ui/common/nodeList";
 | 
			
		||||
@import "ui/common/checkboxSet";
 | 
			
		||||
@import "ui/common/stack";
 | 
			
		||||
 | 
			
		||||
@import "dragdrop";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								editor/sass/tab-context.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								editor/sass/tab-context.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
.sidebar-context-stack {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
 | 
			
		||||
    .palette-category {
 | 
			
		||||
        &:not(.palette-category-expanded) button {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-context-property {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    .debug-message-tools {
 | 
			
		||||
        right: 0px;
 | 
			
		||||
        margin-right: 5px;
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
    &:hover .debug-message-tools {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.sidebar-context-updated {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
    font-size: 11px;
 | 
			
		||||
    color: #bbb;
 | 
			
		||||
    padding: 1px 3px;
 | 
			
		||||
}
 | 
			
		||||
.sidebar-context-property-storename {
 | 
			
		||||
    display: block;
 | 
			
		||||
    font-size: 0.8em;
 | 
			
		||||
    font-style: italic;
 | 
			
		||||
    color: #aaa;
 | 
			
		||||
}
 | 
			
		||||
@@ -87,17 +87,22 @@ table.node-info tr.blank {
 | 
			
		||||
        padding-left: 5px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.node-info-none {
 | 
			
		||||
    font-style: italic;
 | 
			
		||||
    color: #aaa;
 | 
			
		||||
}
 | 
			
		||||
table.node-info tr:not(.blank) td:first-child{
 | 
			
		||||
    color: #666;
 | 
			
		||||
    color: #444;
 | 
			
		||||
    vertical-align: top;
 | 
			
		||||
    width: 90px;
 | 
			
		||||
    padding: 3px 3px 3px 6px;
 | 
			
		||||
    background:#f9f9f9;
 | 
			
		||||
    border-right: 1px solid #ddd;
 | 
			
		||||
}
 | 
			
		||||
table.node-info tr:not(.blank) td:last-child{
 | 
			
		||||
    padding: 3px 3px 3px 6px;
 | 
			
		||||
    color: #666;
 | 
			
		||||
    overflow-y: hidden;
 | 
			
		||||
}
 | 
			
		||||
div.node-info {
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@
 | 
			
		||||
                        color: $workspace-button-color-hover;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .red-ui-tab-icon {
 | 
			
		||||
                img.red-ui-tab-icon {
 | 
			
		||||
                    opacity: 0.2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -113,6 +113,21 @@
 | 
			
		||||
    &.red-ui-tabs-add.red-ui-tabs-scrollable {
 | 
			
		||||
        padding-right: 59px;
 | 
			
		||||
    }
 | 
			
		||||
    &.red-ui-tabs-collapsible {
 | 
			
		||||
        li:not(.active) {
 | 
			
		||||
            display: none;
 | 
			
		||||
             &.red-ui-tab-pinned {
 | 
			
		||||
                 a {
 | 
			
		||||
                     padding-left: 0;
 | 
			
		||||
                     text-align: center;
 | 
			
		||||
                 }
 | 
			
		||||
                 span {
 | 
			
		||||
                     display: none;
 | 
			
		||||
                 }
 | 
			
		||||
                 width: 32px;
 | 
			
		||||
             }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.red-ui-tabs-vertical {
 | 
			
		||||
        box-sizing: border-box;
 | 
			
		||||
@@ -157,6 +172,15 @@
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-tabs-select {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top:0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        opacity: 0.4;
 | 
			
		||||
        background: red;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-tab-button {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
@@ -180,7 +204,32 @@
 | 
			
		||||
        z-index: 2;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-tab-link-buttons {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    height: 35px;
 | 
			
		||||
    background: #fff;
 | 
			
		||||
    border-bottom: 1px solid $primary-border-color;
 | 
			
		||||
    z-index: 2;
 | 
			
		||||
    a {
 | 
			
		||||
        @include workspace-button-toggle;
 | 
			
		||||
        line-height: 26px;
 | 
			
		||||
        height: 28px;
 | 
			
		||||
        width: 28px;
 | 
			
		||||
        margin: 4px 3px 3px;
 | 
			
		||||
        z-index: 2;
 | 
			
		||||
        &.red-ui-tab-link-button {
 | 
			
		||||
            &:not(.active) {
 | 
			
		||||
                background: #eee;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        &.red-ui-tab-link-button-menu {
 | 
			
		||||
            border-color: white;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-tab-scroll {
 | 
			
		||||
    width: 21px;
 | 
			
		||||
    top: 0;
 | 
			
		||||
@@ -216,7 +265,7 @@
 | 
			
		||||
    right: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-tab-icon {
 | 
			
		||||
img.red-ui-tab-icon {
 | 
			
		||||
    margin-left: -8px;
 | 
			
		||||
    margin-right: 3px;
 | 
			
		||||
    margin-top: -2px;
 | 
			
		||||
@@ -225,6 +274,11 @@
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
i.red-ui-tab-icon {
 | 
			
		||||
    opacity: 0.7;
 | 
			
		||||
    width: 18px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-tabs-badges {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								editor/sass/ui/common/stack.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								editor/sass/ui/common/stack.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
.red-ui-stack {
 | 
			
		||||
    background: white;
 | 
			
		||||
    .palette-category {
 | 
			
		||||
        background: white;
 | 
			
		||||
 | 
			
		||||
        &:last-child {
 | 
			
		||||
            border-bottom: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    overflow:hidden;
 | 
			
		||||
    overflow:visible;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    .red-ui-typedInput-input {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
        border-bottom-left-radius: 0;
 | 
			
		||||
        box-shadow: none;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        // backgroun/d: #f0fff0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.red-ui-typedInput-focus:not(.input-error) {
 | 
			
		||||
@@ -63,10 +64,11 @@
 | 
			
		||||
        line-height: 32px;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        color: #555;
 | 
			
		||||
        i {
 | 
			
		||||
            position:relative;
 | 
			
		||||
            top:-3px;
 | 
			
		||||
            margin-right:4px;
 | 
			
		||||
        i.red-ui-typedInput-icon {
 | 
			
		||||
            position: relative;
 | 
			
		||||
            top: -3px;
 | 
			
		||||
            margin-left: 1px;
 | 
			
		||||
            margin-right: 2px;
 | 
			
		||||
            margin-top: 1px;
 | 
			
		||||
            vertical-align: middle;
 | 
			
		||||
            &.fa-ellipsis-h {
 | 
			
		||||
@@ -75,11 +77,11 @@
 | 
			
		||||
        }
 | 
			
		||||
        &.disabled {
 | 
			
		||||
            cursor: default;
 | 
			
		||||
            i {
 | 
			
		||||
            i.red-ui-typedInput-icon {
 | 
			
		||||
                color: #bbb;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        span {
 | 
			
		||||
        .red-ui-typedInput-type-label,.red-ui-typedInput-option-label {
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            padding: 0 1px 0 5px;
 | 
			
		||||
@@ -120,26 +122,25 @@
 | 
			
		||||
        border-bottom-right-radius: 4px;
 | 
			
		||||
        padding: 0 0 0 0;
 | 
			
		||||
        position:absolute;
 | 
			
		||||
        width: calc( 100% );
 | 
			
		||||
 | 
			
		||||
        i {
 | 
			
		||||
            position:absolute;
 | 
			
		||||
            right: 4px;
 | 
			
		||||
            top: 7px;
 | 
			
		||||
        }
 | 
			
		||||
        right: 0;
 | 
			
		||||
        .red-ui-typedInput-option-label {
 | 
			
		||||
            background:#fff;
 | 
			
		||||
            background:$typedInput-button-background;
 | 
			
		||||
            position:absolute;
 | 
			
		||||
            left:0;
 | 
			
		||||
            right:23px;
 | 
			
		||||
            top: 0;
 | 
			
		||||
            padding: 0 5px 0 5px;
 | 
			
		||||
            padding: 0 5px 0 8px;
 | 
			
		||||
            i.red-ui-typedInput-icon {
 | 
			
		||||
                margin-right: 4px;
 | 
			
		||||
                margin-top: 4px;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .red-ui-typedInput-option-caret {
 | 
			
		||||
            top: 0;
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            right: 0;
 | 
			
		||||
            width: 17px;
 | 
			
		||||
            padding-left: 6px;
 | 
			
		||||
        }
 | 
			
		||||
        &:focus {
 | 
			
		||||
            box-shadow: none;
 | 
			
		||||
@@ -174,4 +175,7 @@
 | 
			
		||||
            background: $typedInput-button-background-active;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-typedInput-icon {
 | 
			
		||||
        margin-right: 4px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,22 +38,52 @@
 | 
			
		||||
    label {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        min-width: 100px;
 | 
			
		||||
        vertical-align: top;
 | 
			
		||||
        margin-top: 5px;
 | 
			
		||||
        input {
 | 
			
		||||
            vertical-align: top;
 | 
			
		||||
            padding-bottom: 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    input {
 | 
			
		||||
        margin-bottom: 0;
 | 
			
		||||
    input, div.uneditable-input {
 | 
			
		||||
        //margin-bottom: 0;
 | 
			
		||||
    }
 | 
			
		||||
    div.uneditable-input {
 | 
			
		||||
        position: relative;
 | 
			
		||||
    }
 | 
			
		||||
    input[type='number'] {
 | 
			
		||||
        width: 60px;
 | 
			
		||||
    }
 | 
			
		||||
    h4 {
 | 
			
		||||
        margin-top: 20px;
 | 
			
		||||
        margin-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#user-settings-tab-view {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top:0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    padding: 8px 20px 20px;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
.user-settings-row {
 | 
			
		||||
    padding: 5px 10px 2px;
 | 
			
		||||
}
 | 
			
		||||
.user-settings-section {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    &:after {
 | 
			
		||||
        content: "";
 | 
			
		||||
        display: table;
 | 
			
		||||
        clear: both;
 | 
			
		||||
    }
 | 
			
		||||
    .uneditable-input, input, textarea {
 | 
			
		||||
        width: calc(100% - 150px);
 | 
			
		||||
    }
 | 
			
		||||
    textarea {
 | 
			
		||||
        resize: none;
 | 
			
		||||
        height: 10em;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,9 @@
 | 
			
		||||
.workspace-footer-button {
 | 
			
		||||
    @include component-footer-button;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-footer-button-toggle {
 | 
			
		||||
    @include component-footer-button-toggle;
 | 
			
		||||
}
 | 
			
		||||
#workspace-footer {
 | 
			
		||||
    @include component-footer;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,7 @@
 | 
			
		||||
            <a class="workspace-footer-button" id="btn-zoom-out" href="#"><i class="fa fa-minus"></i></a>
 | 
			
		||||
            <a class="workspace-footer-button" id="btn-zoom-zero" href="#"><i class="fa fa-circle-o"></i></a>
 | 
			
		||||
            <a class="workspace-footer-button" id="btn-zoom-in" href="#"><i class="fa fa-plus"></i></a>
 | 
			
		||||
            <a class="workspace-footer-button-toggle single" id="btn-navigate" href="#"><i class="fa fa-map-o"></i></a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="editor-shade" class="hide"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -77,35 +78,11 @@
 | 
			
		||||
    <div id="sidebar-separator"></div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
<div id="full-shade" class="hide"></div>
 | 
			
		||||
 | 
			
		||||
<div id="notifications"></div>
 | 
			
		||||
<div id="dropTarget"><div data-i18n="[append]workspace.dropFlowHere"><br/><i class="fa fa-download"></i></div></div>
 | 
			
		||||
 | 
			
		||||
<div id="node-dialog-confirm-deploy" class="hide">
 | 
			
		||||
    <form class="form-horizontal">
 | 
			
		||||
        <div id="node-dialog-confirm-deploy-config" class="node-dialog-confirm-row" data-i18n="[prepend]deploy.confirm.improperlyConfigured;[append]deploy.confirm.confirm">
 | 
			
		||||
            <ul id="node-dialog-confirm-deploy-invalid-list"></ul>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="node-dialog-confirm-deploy-unknown" class="node-dialog-confirm-row" data-i18n="[prepend]deploy.confirm.unknown;[append]deploy.confirm.confirm">
 | 
			
		||||
            <ul id="node-dialog-confirm-deploy-unknown-list"></ul>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="node-dialog-confirm-deploy-conflict" class="node-dialog-confirm-row">
 | 
			
		||||
            <div style="margin-left: 40px; margin-bottom: 10px;">
 | 
			
		||||
                <span data-i18n="deploy.confirm.conflict"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="node-dialog-confirm-deploy-conflict-checking" class="node-dialog-confirm-conflict-row">
 | 
			
		||||
                <img src="red/images/spin.svg"/><div data-i18n="deploy.confirm.conflictChecking"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="node-dialog-confirm-deploy-conflict-auto-merge" class="node-dialog-confirm-conflict-row">
 | 
			
		||||
                <i style="color: #3a3;" class="fa fa-check"></i><div data-i18n="deploy.confirm.conflictAutoMerge"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="node-dialog-confirm-deploy-conflict-manual-merge" class="node-dialog-confirm-conflict-row">
 | 
			
		||||
                <i style="color: #999;" class="fa fa-exclamation"></i><div data-i18n="deploy.confirm.conflictManualMerge"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="node-dialog-library-save-confirm" class="hide">
 | 
			
		||||
    <form class="form-horizontal">
 | 
			
		||||
        <div style="text-align: center; padding-top: 30px;" id="node-dialog-library-save-content">
 | 
			
		||||
@@ -155,6 +132,10 @@
 | 
			
		||||
        <i class="fa fa-tag"></i>
 | 
			
		||||
        <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="form-row">
 | 
			
		||||
        <i class="fa fa-folder-o"></i>
 | 
			
		||||
        <label for="subflow-input-category" data-i18n="editor:subflow.category"></label><select style="width: 250px;" id="subflow-input-category"></select><input style="display:none; margin-left: 10px; width:calc(100% - 250px)" type="text" id="subflow-input-custom-category">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="form-row" style="margin-bottom: 0px;">
 | 
			
		||||
        <label for="subflow-input-info" data-i18n="editor:subflow.info"></label>
 | 
			
		||||
        <a href="https://help.github.com/articles/markdown-basics/" style="font-size: 0.8em; float: right;" data-i18n="[html]subflow.format"></a>
 | 
			
		||||
@@ -164,69 +145,6 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="form-row form-tips" id="subflow-dialog-user-count"></div>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/x-red" data-template-name="_expression">
 | 
			
		||||
    <div id="node-input-expression-panels">
 | 
			
		||||
        <div id="node-input-expression-panel-expr" class="red-ui-panel">
 | 
			
		||||
            <div class="form-row" style="margin-bottom: 3px; text-align: right;">
 | 
			
		||||
                <span class="node-input-expression-legacy"><i class="fa fa-exclamation-circle"></i> <span data-i18n="expressionEditor.compatMode"></span></span>
 | 
			
		||||
                <button id="node-input-expression-reformat" class="editor-button editor-button-small"><span data-i18n="expressionEditor.format"></span></button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-row node-text-editor-row">
 | 
			
		||||
                <div class="node-text-editor" id="node-input-expression"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="node-input-expression-panel-info" class="red-ui-panel">
 | 
			
		||||
            <div class="form-row">
 | 
			
		||||
                <ul id="node-input-expression-tabs"></ul>
 | 
			
		||||
                <div id="node-input-expression-tab-help" class="node-input-expression-tab-content hide">
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <select id="node-input-expression-func"></select>
 | 
			
		||||
                        <button id="node-input-expression-func-insert" class="editor-button" data-i18n="expressionEditor.insert"></button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="node-input-expression-help"></div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div id="node-input-expression-tab-test" class="node-input-expression-tab-content hide">
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <span style="display: inline-block; width: calc(50% - 5px);">
 | 
			
		||||
                            <span data-i18n="expressionEditor.data"></span>
 | 
			
		||||
                            <button style="float: right; margin-right: 5px;" id="node-input-example-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button>
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <span style="display: inline-block; width: calc(50% - 5px);" data-i18n="expressionEditor.result"></span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-data"></div>
 | 
			
		||||
                    <div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-result"></div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
<script type="text/x-red" data-template-name="_json">
 | 
			
		||||
    <div class="form-row" style="margin-bottom: 3px; text-align: right;">
 | 
			
		||||
        <button id="node-input-json-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="form-row node-text-editor-row">
 | 
			
		||||
        <div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
<script type="text/x-red" data-template-name="_buffer">
 | 
			
		||||
    <div id="node-input-buffer-panels">
 | 
			
		||||
        <div id="node-input-buffer-panel-str" class="red-ui-panel">
 | 
			
		||||
            <div class="form-row" style="margin-bottom: 3px; text-align: right;">
 | 
			
		||||
                <span class="node-input-buffer-type"><i class="fa fa-exclamation-circle"></i> <span id="node-input-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="node-input-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-row node-text-editor-row">
 | 
			
		||||
                <div class="node-text-editor" id="node-input-buffer-str"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="node-input-buffer-panel-bin" class="red-ui-panel">
 | 
			
		||||
            <div class="form-row node-text-editor-row" style="margin-top: 10px">
 | 
			
		||||
                <div class="node-text-editor" id="node-input-buffer-bin"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script src="vendor/vendor.js"></script>
 | 
			
		||||
<script src="vendor/jsonata/jsonata.min.js"></script>
 | 
			
		||||
<script src="vendor/ace/ace.js"></script>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								editor/vendor/ace/ace.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/ace.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/ext-language_tools.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/ext-language_tools.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/ext-searchbox.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/ext-searchbox.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-css.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-css.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-handlebars.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-handlebars.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-html.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-html.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-javascript.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-javascript.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-json.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-json.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-markdown.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-markdown.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										0
									
								
								editor/vendor/ace/mode-properties.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								editor/vendor/ace/mode-properties.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										1
									
								
								editor/vendor/ace/mode-python.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								editor/vendor/ace/mode-python.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
ace.define("ace/mode/python_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield|async|await",t="True|False|None|NotImplemented|Ellipsis|__debug__",n="abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern",r=this.createKeywordMapper({"invalid.deprecated":"debugger","support.function":n,"constant.language":t,keyword:e},"identifier"),i="(?:r|u|ur|R|U|UR|Ur|uR)?",s="(?:(?:[1-9]\\d*)|(?:0))",o="(?:0[oO]?[0-7]+)",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:0[bB][01]+)",f="(?:"+s+"|"+o+"|"+u+"|"+a+")",l="(?:[eE][+-]?\\d+)",c="(?:\\.\\d+)",h="(?:\\d+)",p="(?:(?:"+h+"?"+c+")|(?:"+h+"\\.))",d="(?:(?:"+p+"|"+h+")"+l+")",v="(?:"+d+"|"+p+")",m="\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:i+'"{3}',next:"qqstring3"},{token:"string",regex:i+'"(?=.)',next:"qqstring"},{token:"string",regex:i+"'{3}",next:"qstring3"},{token:"string",regex:i+"'(?=.)",next:"qstring"},{token:"constant.numeric",regex:"(?:"+v+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:v},{token:"constant.numeric",regex:f+"[lL]\\b"},{token:"constant.numeric",regex:f+"\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}],qqstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.PythonHighlightRules=s}),ace.define("ace/mode/folding/pythonic",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){this.foldingStartMarker=new RegExp("([\\[{])(?:\\s*)$|("+e+")(?:\\s*)(?:#.*)?$")};r.inherits(s,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i)return i[1]?this.openingBracketBlock(e,i[1],n,i.index):i[2]?this.indentationBlock(e,n,i.index+i[2].length):this.indentationBlock(e,n)}}.call(s.prototype)}),ace.define("ace/mode/python",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/python_highlight_rules","ace/mode/folding/pythonic","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./python_highlight_rules").PythonHighlightRules,o=e("./folding/pythonic").FoldMode,u=e("../range").Range,a=function(){this.HighlightRules=s,this.foldingRules=new o("\\:"),this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.getTokenizer().getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new u(n,r.length-i.length,n,r.length))},this.$id="ace/mode/python"}.call(a.prototype),t.Mode=a})
 | 
			
		||||
							
								
								
									
										0
									
								
								editor/vendor/ace/mode-sql.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								editor/vendor/ace/mode-sql.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-swift.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-swift.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								editor/vendor/ace/mode-yaml.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								editor/vendor/ace/mode-yaml.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							@@ -1 +1 @@
 | 
			
		||||
ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?:\s+|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?:\s+|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d\s]*$/,onMatch:function(e,t,n,r){var i=/^\s*/.exec(r)[0];return n.length<1?n.push(this.next):n[0]="mlString",n.length<2?n.push(i.length):n[1]=i.length,this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"}],mlString:[{token:"indent",regex:/^\s*$/},{token:"indent",regex:/^\s*/,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/yaml"}.call(a.prototype),t.Mode=a})
 | 
			
		||||
ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d\s]*$/,onMatch:function(e,t,n,r){var i=/^\s*/.exec(r)[0];return n.length<1?n.push(this.next):n[0]="mlString",n.length<2?n.push(i.length):n[1]=i.length,this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlString:[{token:"indent",regex:/^\s*$/},{token:"indent",regex:/^\s*/,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart=["#","//"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/yaml"}.call(a.prototype),t.Mode=a})
 | 
			
		||||
							
								
								
									
										0
									
								
								editor/vendor/ace/snippets/css.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								editor/vendor/ace/snippets/css.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										0
									
								
								editor/vendor/ace/snippets/handlebars.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								editor/vendor/ace/snippets/handlebars.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										836
									
								
								editor/vendor/ace/snippets/html.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										836
									
								
								editor/vendor/ace/snippets/html.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										0
									
								
								editor/vendor/ace/snippets/javascript.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								editor/vendor/ace/snippets/javascript.js
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										1
									
								
								editor/vendor/ace/snippets/json.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								editor/vendor/ace/snippets/json.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
ace.define("ace/snippets/json",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope="json"})
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user