mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Compare commits
	
		
			1255 Commits
		
	
	
		
			1.3.4
			...
			3.0.0-beta
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 98b4b0dce0 | ||
|  | 70f26e0bea | ||
|  | b6ad396a6c | ||
|  | e44bb57b0e | ||
|  | 5c10b16b65 | ||
|  | 7ec1d42808 | ||
|  | 03e9e89558 | ||
|  | 3057035dec | ||
|  | 4af72cc7ba | ||
|  | f6aee81651 | ||
|  | a22f569ca0 | ||
|  | 8043f5d865 | ||
|  | 2ef50ab71f | ||
|  | 192a4f5e7f | ||
|  | 1af56a7f00 | ||
|  | d2fab7fddd | ||
|  | 1818b0281d | ||
|  | 09973ba8cf | ||
|  | dd3174c40f | ||
|  | f0293b8f52 | ||
|  | d549a9ad92 | ||
|  | 0385c72a8f | ||
|  | e223b20cbd | ||
|  | a0f7e92e40 | ||
|  | c87ff3ca26 | ||
|  | 82672a825d | ||
|  | 98d524e82d | ||
|  | 3d3090a8f2 | ||
|  | 5561e89201 | ||
|  | 9ed96de237 | ||
|  | 0f2420576a | ||
|  | d1b74675d9 | ||
|  | abb81a0bac | ||
|  | 05eb055b8c | ||
|  | e8ddd6d16d | ||
|  | 8706998c8c | ||
|  | 06e0869767 | ||
|  | 7841fc6d3e | ||
|  | 1f7311deeb | ||
|  | 07a9e69e7b | ||
|  | 9bc8adc715 | ||
|  | 7845ebffc5 | ||
|  | b985de6df2 | ||
|  | 11f6491889 | ||
|  | b2ec040a8d | ||
|  | 424a53da4e | ||
|  | 963c289af7 | ||
|  | c5af71e0a2 | ||
|  | 329008bf6d | ||
|  | 531dbc5f83 | ||
|  | 851a925956 | ||
|  | 5d4e01eea6 | ||
|  | 7484dc5b4c | ||
|  | e04f5cb277 | ||
|  | c513cff843 | ||
|  | a4603a4396 | ||
|  | bc5eafce66 | ||
|  | 5fb811eb4c | ||
|  | 84a3884ffc | ||
|  | 50ae29a08c | ||
|  | bf8bfa582a | ||
|  | 492d1ef30e | ||
|  | bd19c203e1 | ||
|  | 1141f9de86 | ||
|  | 7955a17a17 | ||
|  | 58085e39d1 | ||
|  | bbc32c4cd0 | ||
|  | 3841039728 | ||
|  | f04d954882 | ||
|  | 39602ff5f2 | ||
|  | 55ecc7a92c | ||
|  | 1148960d43 | ||
|  | 415107fbf0 | ||
|  | 437cc20198 | ||
|  | 32f78a99fd | ||
|  | 65c7855afd | ||
|  | 7f68e341da | ||
|  | a2f750ed1a | ||
|  | b74a42cdf5 | ||
|  | 95b35be541 | ||
|  | ea747a3d58 | ||
|  | 9b644e3c47 | ||
|  | 3f776397d1 | ||
|  | 1ec75035ba | ||
|  | 97b7b7b968 | ||
|  | 135427dcc8 | ||
|  | a2de514c05 | ||
|  | f1bada7fd8 | ||
|  | ea5d25c794 | ||
|  | 193e420eb3 | ||
|  | 8a972ee543 | ||
|  | b51eb7326f | ||
|  | be3b5b7fe2 | ||
|  | 3a7a606f6a | ||
|  | 294fc6b62f | ||
|  | 662a44fccf | ||
|  | b0a5d4fb6f | ||
|  | 4fb8292618 | ||
|  | 539e5899e3 | ||
|  | bee9e20827 | ||
|  | 12f527a120 | ||
|  | bd626899df | ||
|  | 54d036715f | ||
|  | 54c87f81a6 | ||
|  | 5de078dc61 | ||
|  | ff57de0753 | ||
|  | 9565aee3c5 | ||
|  | f63da0c58b | ||
|  | 205dbc1a25 | ||
|  | 12c309fd50 | ||
|  | f04e3d5338 | ||
|  | dc03d0b300 | ||
|  | 3e16cc4912 | ||
|  | fcdf252f03 | ||
|  | b23fea9cb5 | ||
|  | e714ff35c4 | ||
|  | 4054d0eca7 | ||
|  | 367f9b6232 | ||
|  | 194eb4e266 | ||
|  | f717eb7388 | ||
|  | e8f20285af | ||
|  | 0fec9c7c55 | ||
|  | f8d0ed7ca6 | ||
|  | 020eaef5ba | ||
|  | 973b31521e | ||
|  | 59e513f130 | ||
|  | 62e730b621 | ||
|  | f4bb62adbc | ||
|  | 48a528a4b8 | ||
|  | 5aba66ea78 | ||
|  | c5efdf5ae3 | ||
|  | 15958cd4a3 | ||
|  | edcdc6c97c | ||
|  | 143b807e9b | ||
|  | 861379c227 | ||
|  | 2fb9f62d0b | ||
|  | 84e02fc144 | ||
|  | 57ac90f837 | ||
|  | 10a45ece76 | ||
|  | c88a177cb2 | ||
|  | a63dfc4650 | ||
|  | 8924ac2783 | ||
|  | 4fffa2d0ba | ||
|  | 856d2ab266 | ||
|  | 750d2c76f5 | ||
|  | 47157049c0 | ||
|  | 22000f10df | ||
|  | d802ce1484 | ||
|  | 4b10b9ffc3 | ||
|  | 552408f488 | ||
|  | 4884938036 | ||
|  | cdcc8cc59a | ||
|  | f7bd600715 | ||
|  | 0014fec63f | ||
|  | 812efde342 | ||
|  | 889f0e1569 | ||
|  | 3a26c5cd65 | ||
|  | 12a25c37aa | ||
|  | 4706e20a1d | ||
|  | 14c23051ee | ||
|  | 330ddfa3ad | ||
|  | a1e9a14ef3 | ||
|  | 958f57085f | ||
|  | 646a786b75 | ||
|  | c8516bc5f4 | ||
|  | 58e87b3ddf | ||
|  | ea0abb70a2 | ||
|  | 908f9562f6 | ||
|  | 8131d9a640 | ||
|  | e092f41074 | ||
|  | 79a90dc476 | ||
|  | 25962dbf39 | ||
|  | 8df53e441d | ||
|  | 6f89efa40b | ||
|  | d6a1b4e71f | ||
|  | a2c0e53f87 | ||
|  | 013af7619e | ||
|  | aa302ecc32 | ||
|  | 99b049fe2d | ||
|  | dbdd1b8671 | ||
|  | 8ba6a7436e | ||
|  | 6e35a9f682 | ||
|  | 78f456911a | ||
|  | 8f5d3dc49c | ||
|  | 97678577fb | ||
|  | ce67737cc9 | ||
|  | b9919b0a9c | ||
|  | 226f45d8d5 | ||
|  | accbf6ecfc | ||
|  | 0aa80d82d9 | ||
|  | ace5f81a17 | ||
|  | 21a0b33645 | ||
|  | 8b991e11a2 | ||
|  | 8f013776df | ||
|  | c30aedd309 | ||
|  | 39f303fcd6 | ||
|  | 76c0e140cf | ||
|  | ccb3c991a6 | ||
|  | a7932da207 | ||
|  | 5fda20c330 | ||
|  | 1be6e4565f | ||
|  | e606d0b1de | ||
|  | b4bcb7ace2 | ||
|  | a63fee1223 | ||
|  | ae76ff0aaf | ||
|  | bba819ba84 | ||
|  | d0d0da6cb7 | ||
|  | 475113838a | ||
|  | b8435efc97 | ||
|  | 780e41d6a6 | ||
|  | 6d0b55f753 | ||
|  | c9fa5c7284 | ||
|  | e97f4c4054 | ||
|  | 96d15b7505 | ||
|  | 2f77596034 | ||
|  | 5619c105aa | ||
|  | 9a6ee023b3 | ||
|  | 77e2e44abc | ||
|  | 32bddfdd47 | ||
|  | c9aa654ef0 | ||
|  | 5e501857aa | ||
|  | 6b00aba039 | ||
|  | 14fa9cfa4b | ||
|  | 5633c5224e | ||
|  | 03763a1423 | ||
|  | cf6df1556c | ||
|  | cdc8a42393 | ||
|  | a2fd705153 | ||
|  | 3388f699a0 | ||
|  | 5e197713ff | ||
|  | 50718495da | ||
|  | b7b604aed4 | ||
|  | a1f5cabbba | ||
|  | c2aae6ddf6 | ||
|  | 8a40622815 | ||
|  | cb88409102 | ||
|  | b918b75414 | ||
|  | 37f0e36c98 | ||
|  | c8dc2327a3 | ||
|  | f660973168 | ||
|  | cf2e7744f3 | ||
|  | 855d799b21 | ||
|  | 97dd1d0f4f | ||
|  | 5b5553b9a3 | ||
|  | 702545e0b2 | ||
|  | fa2787eb5d | ||
|  | 0f37b326a0 | ||
|  | 7f9f551cfe | ||
|  | ecf1847dd2 | ||
|  | 40a9dce869 | ||
|  | a6696733fa | ||
|  | d9bd736159 | ||
|  | 4f5f5d31a3 | ||
|  | fad1325427 | ||
|  | 8b6678a453 | ||
|  | c7f48a83c0 | ||
|  | af0f02d63e | ||
|  | 497d63e67e | ||
|  | 4d048af384 | ||
|  | 3649f10600 | ||
|  | 49e69a54bd | ||
|  | a0acc89fcb | ||
|  | 64d1a82920 | ||
|  | 2396e28479 | ||
|  | db1ad0df63 | ||
|  | c5de18caae | ||
|  | e57774e121 | ||
|  | b7ee46d400 | ||
|  | 6007132640 | ||
|  | 31b3a4c342 | ||
|  | 73ff852648 | ||
|  | 6d50eb5737 | ||
|  | 3c0b74005b | ||
|  | 93ff667df1 | ||
|  | a8579fa68a | ||
|  | 10f77fdf1a | ||
|  | 99d824a999 | ||
|  | 97d2b5df15 | ||
|  | 84a9cf7adf | ||
|  | a49927f173 | ||
|  | 6a5c50ff77 | ||
|  | c948573c2d | ||
|  | 5233bc501c | ||
|  | ad96c6f838 | ||
|  | 0e92f68b4a | ||
|  | ac97e8c613 | ||
|  | 95fe717ca7 | ||
|  | 10b18de3e0 | ||
|  | 08295eb807 | ||
|  | fce4f0c116 | ||
|  | cf1424976f | ||
|  | 94e8fce40a | ||
|  | ea671bf395 | ||
|  | fdb868516f | ||
|  | c75bebfc90 | ||
|  | 3e102ef760 | ||
|  | c07eddbd97 | ||
|  | e85b925f40 | ||
|  | 27761ba6f2 | ||
|  | b665698e78 | ||
|  | 97ebe33d68 | ||
|  | 249f7e45fb | ||
|  | c0612e6193 | ||
|  | 79a789c557 | ||
|  | 450888f542 | ||
|  | 380a08242a | ||
|  | e653a933f1 | ||
|  | bda5dffa34 | ||
|  | 29df7e84a1 | ||
|  | 19cf43a10e | ||
|  | 0398ef3b90 | ||
|  | 8c19daf949 | ||
|  | e4f0688a02 | ||
|  | 25f4fbf2bb | ||
|  | 0533c08438 | ||
|  | 5f0ea85f47 | ||
|  | 6c7c1202ed | ||
|  | 669aa769c2 | ||
|  | fcf2994015 | ||
|  | bee21ddc9e | ||
|  | 263e68e677 | ||
|  | 2b958f5724 | ||
|  | 95f7177ef4 | ||
|  | 006324b78e | ||
|  | efd8c1229d | ||
|  | 0f1aea3e0d | ||
|  | 6a41cbebc9 | ||
|  | fba95e6a42 | ||
|  | bffb91f196 | ||
|  | 4573b65639 | ||
|  | 3d8505385a | ||
|  | 95d3a8cc22 | ||
|  | 99c053f86b | ||
|  | f2dde705ef | ||
|  | be11fda814 | ||
|  | 0261105c52 | ||
|  | 264047dc0c | ||
|  | 4d84926ed2 | ||
|  | da3211fee6 | ||
|  | 1388b03cf2 | ||
|  | 9f98b4b082 | ||
|  | b3f1401ab4 | ||
|  | e5e3832809 | ||
|  | 2eff7da171 | ||
|  | e9622bcfe8 | ||
|  | 63ebadc526 | ||
|  | fe47b07229 | ||
|  | dc73997be3 | ||
|  | 62315fd478 | ||
|  | fb81121bd3 | ||
|  | 5293563a6a | ||
|  | 2e1e61dabe | ||
|  | 280d63fde7 | ||
|  | c6104195f6 | ||
|  | e55cbb3e3d | ||
|  | 56b85e4194 | ||
|  | 6431c43d0e | ||
|  | a62107fbd1 | ||
|  | 7959d18248 | ||
|  | 2f66915a9f | ||
|  | a508177e21 | ||
|  | 5a9d858604 | ||
|  | 514da83961 | ||
|  | b7bae18849 | ||
|  | fbde247c72 | ||
|  | 3a69af9034 | ||
|  | 5c87a6cb76 | ||
|  | 9c6bb434e8 | ||
|  | d1bd303dfa | ||
|  | 3813c32454 | ||
|  | 3b00a692ee | ||
|  | 3304ebe9d3 | ||
|  | 513120cbfe | ||
|  | b06049d5a3 | ||
|  | 0f50355deb | ||
|  | bd6e35fea2 | ||
|  | a4fd63cd44 | ||
|  | fdc4219b68 | ||
|  | 5b428bb8e6 | ||
|  | c948ff88a5 | ||
|  | 9b9a0d7060 | ||
|  | 16578e3677 | ||
|  | be7f84bc67 | ||
|  | 033d26f2cb | ||
|  | 4c0826b1c4 | ||
|  | c4cc204c94 | ||
|  | 3747db18b1 | ||
|  | 4173625fca | ||
|  | 0e7863a6fb | ||
|  | 207ba00ad2 | ||
|  | 87c89586a5 | ||
|  | 1cea1ced82 | ||
|  | 08732bac0f | ||
|  | 283e8d3c08 | ||
|  | 42a7165596 | ||
|  | aa3f5001d5 | ||
|  | 56580c4005 | ||
|  | ba304c9651 | ||
|  | 703c5adba7 | ||
|  | 8b85f6e0a6 | ||
|  | c136d22382 | ||
|  | 42358419ad | ||
|  | ab2ced5c37 | ||
|  | faf31be0dc | ||
|  | ff4c67d068 | ||
|  | 10b133db02 | ||
|  | f0bf607b43 | ||
|  | 8948ca5323 | ||
|  | 9c3be51fe9 | ||
|  | 2da9161f29 | ||
|  | 983dad5b53 | ||
|  | 8b23d341b4 | ||
|  | 0ad60013aa | ||
|  | 289815e128 | ||
|  | f7ee83f1b9 | ||
|  | f67aafa8d3 | ||
|  | 4e5ddd57bf | ||
|  | e0d4ecf835 | ||
|  | 81a461115b | ||
|  | d679b02658 | ||
|  | 5effcdb024 | ||
|  | 211a5eb2bb | ||
|  | c4465ba58d | ||
|  | 7903c53876 | ||
|  | dbefe6a560 | ||
|  | 8b1f412255 | ||
|  | a2e0074061 | ||
|  | 5fc920087b | ||
|  | 085233ab9b | ||
|  | 6657b2629f | ||
|  | 310a279aaf | ||
|  | 58f3a76da7 | ||
|  | a2c9458b1b | ||
|  | 75bcd9e8d5 | ||
|  | 977e7ef395 | ||
|  | eb1b8b577f | ||
|  | 28f91685ce | ||
|  | 81a4fe59d9 | ||
|  | e7189ab81f | ||
|  | 346db89e66 | ||
|  | f786c7f144 | ||
|  | 51f45293b8 | ||
|  | 943b103001 | ||
|  | f055d42277 | ||
|  | fb7a2a8d5d | ||
|  | 1e5ed2a2e3 | ||
|  | e1467dfe23 | ||
|  | c480f96d30 | ||
|  | cf613aafb2 | ||
|  | 20dbf7c5f4 | ||
|  | 47c912c25b | ||
|  | 48fb1a8127 | ||
|  | 6c1b55db16 | ||
|  | df70c8a800 | ||
|  | 82ae2e7118 | ||
|  | 0cf9b5f3df | ||
|  | 036a825892 | ||
|  | 8b43b31c64 | ||
|  | 3abef972a7 | ||
|  | 30b00741b5 | ||
|  | f86e743cce | ||
|  | cb96fb735e | ||
|  | 459a52d31d | ||
|  | 44ef9a13d6 | ||
|  | 0bb3652a63 | ||
|  | 6a82d683a9 | ||
|  | fad708e8de | ||
|  | c6a38b8355 | ||
|  | ee84eb666b | ||
|  | 6d2793cac6 | ||
|  | 86d518fc2e | ||
|  | 25dba1a6d5 | ||
|  | af949c62c2 | ||
|  | ea20342d76 | ||
|  | 7732d52583 | ||
|  | c801bc5e6b | ||
|  | ea43729063 | ||
|  | e26bae8027 | ||
|  | 555f155cad | ||
|  | f8c47f59bc | ||
|  | f3997128b9 | ||
|  | 154a4e23dd | ||
|  | 52e4e0e569 | ||
|  | 30f2b96c68 | ||
|  | 58c94b7773 | ||
|  | 83203d5f5d | ||
|  | f77d161643 | ||
|  | 6580b139c0 | ||
|  | 2743c7c6ac | ||
|  | 062f76214e | ||
|  | ce98ed98a2 | ||
|  | dce9d93f6c | ||
|  | f7e35a6cbe | ||
|  | 931335220f | ||
|  | 3ce35a8a4b | ||
|  | 9ac4e5cf6a | ||
|  | bd77d7eec3 | ||
|  | b5e48aa509 | ||
|  | b14c42b6a4 | ||
|  | ba794ba58c | ||
|  | b00282590d | ||
|  | 44616c6872 | ||
|  | aaa2b4c3db | ||
|  | 8974d8e4df | ||
|  | 699063cbb0 | ||
|  | e76000b713 | ||
|  | 332b372e31 | ||
|  | cb3fcb7bfa | ||
|  | bef641609e | ||
|  | 24b52f09df | ||
|  | 942b17b807 | ||
|  | cf19d7f3ad | ||
|  | ebd62a4112 | ||
|  | 9af7357ca4 | ||
|  | 0dbc35c252 | ||
|  | c9f03f1ac5 | ||
|  | 02bd292b8c | ||
|  | e5f1029d0c | ||
|  | cae247160f | ||
|  | 6692b1992c | ||
|  | 0937837b7f | ||
|  | 828888490a | ||
|  | 91cb6ba73b | ||
|  | 4ee4d32b2e | ||
|  | 8df630a2f5 | ||
|  | 2cad42870e | ||
|  | 43651135f3 | ||
|  | 6ae42eb787 | ||
|  | ecaf866613 | ||
|  | 5ea3329b36 | ||
|  | 7bb7149f4c | ||
|  | 5856d043ca | ||
|  | 7cd3e49f04 | ||
|  | 173e75175e | ||
|  | 800006dd76 | ||
|  | a824b6910a | ||
|  | dcea382b38 | ||
|  | d9f976baea | ||
|  | 1fa13efe19 | ||
|  | 33af5cd7c6 | ||
|  | 7cb8f97ef1 | ||
|  | bf965a9cde | ||
|  | 17ffff685a | ||
|  | ae76271cff | ||
|  | 8f3a96d615 | ||
|  | 30e750dfe5 | ||
|  | 682dff7c6f | ||
|  | 85415eb8a8 | ||
|  | 68a80b9244 | ||
|  | c331da7323 | ||
|  | 04ffa06221 | ||
|  | 1f0690c6ec | ||
|  | 711467abcd | ||
|  | 9439cd0e3d | ||
|  | 314c19650d | ||
|  | ed6afcd802 | ||
|  | 082d4fe8e1 | ||
|  | cd23b44506 | ||
|  | 46b6b024b9 | ||
|  | cb88cc35e5 | ||
|  | 75c0c44809 | ||
|  | a091b82ba9 | ||
|  | a3b8f022e6 | ||
|  | 279fcb7c51 | ||
|  | 49a9376073 | ||
|  | 96840ede56 | ||
|  | 7e7f481f99 | ||
|  | 3edbf52bc6 | ||
|  | fba6e801fc | ||
|  | 720a163273 | ||
|  | a9b12e5172 | ||
|  | 6ac0c0a367 | ||
|  | 300402d253 | ||
|  | 0d9bfae503 | ||
|  | bfe0d3b8a3 | ||
|  | 5fdd9c0546 | ||
|  | b6570a16b8 | ||
|  | 8e2d3ea16f | ||
|  | bc2c81f058 | ||
|  | 3e0f080ea7 | ||
|  | 679e07189d | ||
|  | a38ebef100 | ||
|  | b8ad6475e1 | ||
|  | 0f0cb3ac6d | ||
|  | d3efb9d7cc | ||
|  | 84a237d3f5 | ||
|  | e6de52eede | ||
|  | 98aee964d7 | ||
|  | 570e5442e0 | ||
|  | b77a2dc353 | ||
|  | 87af31de20 | ||
|  | cfe201dbe1 | ||
|  | 6ccdab35e0 | ||
|  | 55b9f36b45 | ||
|  | fbcb1130c9 | ||
|  | d4f7a6d2bc | ||
|  | 8a19f71abe | ||
|  | fb153757b5 | ||
|  | 4f175fc93e | ||
|  | 2d4ca7cec0 | ||
|  | bf0ea89969 | ||
|  | 073f0c2a20 | ||
|  | ba83be9062 | ||
|  | 2e7188ea4f | ||
|  | 5a012182d9 | ||
|  | b855438af6 | ||
|  | 2ffea143e7 | ||
|  | 61d85b49e6 | ||
|  | 35f617e96c | ||
|  | 6b6ad47c35 | ||
|  | e57183ed0e | ||
|  | ecfd61a822 | ||
|  | 153f87704b | ||
|  | 836f7d2163 | ||
|  | d4d6f71cf4 | ||
|  | 42a9da006e | ||
|  | 2bd5c4f527 | ||
|  | 6a49b5c106 | ||
|  | 23e14d1b72 | ||
|  | f4f11c8884 | ||
|  | 2b220abdb7 | ||
|  | c1d947ebe3 | ||
|  | d695cf392e | ||
|  | 21304a695c | ||
|  | fa51b06c46 | ||
|  | 7560bb8d7b | ||
|  | fc9d65abcc | ||
|  | a7413cccd0 | ||
|  | 7610353f07 | ||
|  | d3f978c90c | ||
|  | b55a8ef62a | ||
|  | 8d79deffb5 | ||
|  | 8158487c3e | ||
|  | 0cc061196d | ||
|  | d0ec055222 | ||
|  | ae12ddd32b | ||
|  | 31da3adaa9 | ||
|  | 9fd5213f13 | ||
|  | de882f5849 | ||
|  | fded1e0021 | ||
|  | 6cb06c146d | ||
|  | b8f1386ad0 | ||
|  | 2b38b5ea50 | ||
|  | 2f707a6b16 | ||
|  | d4c2fcd559 | ||
|  | 082970cdb7 | ||
|  | fe97c78977 | ||
|  | 79394aa69f | ||
|  | 21fd6e3c21 | ||
|  | de4944cd83 | ||
|  | 3fde5c27ed | ||
|  | e1d492813e | ||
|  | 48d0ee3b6d | ||
|  | eebb64901c | ||
|  | 60e0ed2af6 | ||
|  | f030694ef4 | ||
|  | e9ed13459a | ||
|  | af1e38fdf7 | ||
|  | b12900e680 | ||
|  | 44aa1f4a5e | ||
|  | 9425548a85 | ||
|  | bfd4fc81fe | ||
|  | 439af2a325 | ||
|  | 3204b04455 | ||
|  | bed1be14ba | ||
|  | 7cd92faf0d | ||
|  | be7e28af5d | ||
|  | 8eaa762ec5 | ||
|  | 953a9f7cd4 | ||
|  | 36f099d68b | ||
|  | c8fd5090bd | ||
|  | 155e1be494 | ||
|  | cf5e125cb3 | ||
|  | 764fc8477d | ||
|  | d35e62f8cf | ||
|  | 904babdd13 | ||
|  | 154d3842a8 | ||
|  | edb8a120bd | ||
|  | cdfeba0b82 | ||
|  | 8ce1465e9f | ||
|  | a296b1c9c8 | ||
|  | bb8d7058a4 | ||
|  | 816cfa1c7e | ||
|  | 53938200fc | ||
|  | 3885bb039d | ||
|  | 4adad6e424 | ||
|  | 5fb9531338 | ||
|  | 273d9c76a7 | ||
|  | 79a1d6c561 | ||
|  | 4b0eb8475d | ||
|  | 3dc874b517 | ||
|  | 8a3da1ce8d | ||
|  | 42d90542b5 | ||
|  | 690a93d82d | ||
|  | 8042fe4e2b | ||
|  | a27ce375db | ||
|  | db3688799d | ||
|  | a88be35292 | ||
|  | e2d7fcbfc2 | ||
|  | 421d155586 | ||
|  | 7f9e318214 | ||
|  | 57386edb7c | ||
|  | 94d5ba4550 | ||
|  | 2a0b4ea828 | ||
|  | 893ef227d4 | ||
|  | 1fe6e5a00d | ||
|  | 6c96cde73c | ||
|  | 2b12834d53 | ||
|  | 8a2e74b3b8 | ||
|  | aa1721ab3d | ||
|  | c0a256306b | ||
|  | f0b03b4ada | ||
|  | 5503f53af2 | ||
|  | a89d294b27 | ||
|  | 012e1cbcc5 | ||
|  | 3759e0f778 | ||
|  | 1c18641699 | ||
|  | e50e2201b1 | ||
|  | 1419729458 | ||
|  | c14177b0e8 | ||
|  | 126df969b3 | ||
|  | f8ee92ba06 | ||
|  | 81a278dd8c | ||
|  | a98013806c | ||
|  | 0171ffac6a | ||
|  | 7544241316 | ||
|  | 061afb3a94 | ||
|  | 8a5eda9c1f | ||
|  | f62040f0ec | ||
|  | f2e51779e4 | ||
|  | da114fa3a5 | ||
|  | 3775a1657b | ||
|  | 2bd7c4bc81 | ||
|  | 253c489a33 | ||
|  | ac84b6fe3f | ||
|  | 8761e61439 | ||
|  | 29e903e1c8 | ||
|  | ec27e19e3f | ||
|  | 5df0dae11a | ||
|  | 7fffc1a36d | ||
|  | f3d0179834 | ||
|  | 8bf69c598a | ||
|  | aa5fad6628 | ||
|  | b0f1fad4e2 | ||
|  | 3b6d0995b4 | ||
|  | 0cbf4ac37d | ||
|  | 9ccffee82c | ||
|  | 1b38e2eedf | ||
|  | ce87abe96e | ||
|  | becbda8483 | ||
|  | da210e2ae4 | ||
|  | d4fc6feeba | ||
|  | f1cbca8d76 | ||
|  | dfd9364061 | ||
|  | 1931395fdb | ||
|  | b01fd24e15 | ||
|  | c9d1329fc2 | ||
|  | ab2d3bfd80 | ||
|  | 36bb172f29 | ||
|  | 01e64be39d | ||
|  | 4ebe160f6c | ||
|  | b427eca21f | ||
|  | 3eb438c8d2 | ||
|  | 068f425833 | ||
|  | b3c84242dc | ||
|  | 24672d91d8 | ||
|  | 4422af26ec | ||
|  | 5329e803e2 | ||
|  | ad542b91fa | ||
|  | e9e03c945b | ||
|  | e20cfb3dae | ||
|  | 48baac916c | ||
|  | adadf38b08 | ||
|  | d4e1469450 | ||
|  | 2c456f044f | ||
|  | 228c15ace3 | ||
|  | a0d15e6e7b | ||
|  | 2fe78cf971 | ||
|  | 5ec3544340 | ||
|  | 5443a17775 | ||
|  | 40d60e4eb3 | ||
|  | f3312a6403 | ||
|  | e638b55b30 | ||
|  | 5caa76a8b3 | ||
|  | 901a5ce9d2 | ||
|  | 6ab74951f4 | ||
|  | c2625d696d | ||
|  | 77fb5ef2ab | ||
|  | c20ca3399e | ||
|  | 5825da9c76 | ||
|  | e3853ae402 | ||
|  | bd142a9710 | ||
|  | 4f23847546 | ||
|  | 85820c571d | ||
|  | d9bed03025 | ||
|  | d6e05962c9 | ||
|  | d32636ed6b | ||
|  | bbf066f030 | ||
|  | a3d2f6592e | ||
|  | 87b6327c5e | ||
|  | abaebb329d | ||
|  | 8970fe412d | ||
|  | 9dc5ae21c4 | ||
|  | 4132fb79a6 | ||
|  | 9a4dc30604 | ||
|  | 192b542fe4 | ||
|  | 490547cd3d | ||
|  | 17f9829498 | ||
|  | 234e77fd06 | ||
|  | 87ac831c8a | ||
|  | 4e92492165 | ||
|  | c3d0b1114f | ||
|  | 4463a7d4ba | ||
|  | 741fe3dd90 | ||
|  | e910f3915d | ||
|  | 39aafc5007 | ||
|  | 9b83afae42 | ||
|  | bdf54f6cff | ||
|  | f2a9887a12 | ||
|  | 4f4d78bfab | ||
|  | 3b460fb8fa | ||
|  | ee15e9acc5 | ||
|  | 48fce35fb3 | ||
|  | 78899378c2 | ||
|  | 67404a327d | ||
|  | 702dfa4b79 | ||
|  | 2144407e41 | ||
|  | b36dd62c50 | ||
|  | 7026df7d96 | ||
|  | ed8e7afdf6 | ||
|  | d78e5932f9 | ||
|  | 26d83bb9ea | ||
|  | 8e89b1bdf2 | ||
|  | c880cc0987 | ||
|  | 0874ba7a03 | ||
|  | 7962278475 | ||
|  | c8949f5eeb | ||
|  | 8108b93c5f | ||
|  | 9dbe531bf7 | ||
|  | 46e2ff1001 | ||
|  | d2cdc67ec7 | ||
|  | 56121203bf | ||
|  | e13133fd2b | ||
|  | f20565fd16 | ||
|  | cf2d5841f5 | ||
|  | 630d2ca926 | ||
|  | 34cb93794c | ||
|  | 122b5ba468 | ||
|  | 401466d6c0 | ||
|  | 7f2627dbc8 | ||
|  | 6aecc3915c | ||
|  | ef1b3aa7f5 | ||
|  | 1aaab2a814 | ||
|  | e93734b209 | ||
|  | 9e5218f6b4 | ||
|  | 711ec39327 | ||
|  | 08049252f2 | ||
|  | f1e7ec0c6b | ||
|  | 23765d9139 | ||
|  | 43febe269c | ||
|  | 40233c7702 | ||
|  | 27ed81614b | ||
|  | 889d23e9bd | ||
|  | f8571023f6 | ||
|  | 6364e00202 | ||
|  | a76c6f86c6 | ||
|  | 555e815402 | ||
|  | 8a1d81989b | ||
|  | ee9234b2c6 | ||
|  | 735b9c5844 | ||
|  | 064f3eb3bc | ||
|  | f1775d4fd1 | ||
|  | a9bc111c4f | ||
|  | c100612473 | ||
|  | 26087f8dc7 | ||
|  | 36e75cb728 | ||
|  | d7a2fc2be4 | ||
|  | 142176f194 | ||
|  | c5892fc17e | ||
|  | 6e69cfbca4 | ||
|  | 775181f761 | ||
|  | 36e83d628e | ||
|  | 5f6fcb2bc0 | ||
|  | 7b106e5650 | ||
|  | 79d9c83a2d | ||
|  | 269669ba28 | ||
|  | 4ef7240598 | ||
|  | efdf689c31 | ||
|  | f7606e92ca | ||
|  | 6750be3ec9 | ||
|  | 68fb5089f8 | ||
|  | a8d093bacd | ||
|  | 233a1995b3 | ||
|  | 8ef3baaffb | ||
|  | c9597b9447 | ||
|  | b2dc1d8b23 | ||
|  | 859c0c7f6c | ||
|  | aaf18e2416 | ||
|  | fd679ef117 | ||
|  | 6cc611b3f1 | ||
|  | 77ee726f66 | ||
|  | dc603b76a4 | ||
|  | bcb3371acc | ||
|  | d14ce7e476 | ||
|  | 4d26b806dd | ||
|  | a2b95dbb39 | ||
|  | 47f7b43bcc | ||
|  | 77fd8c120c | ||
|  | a1a6f40158 | ||
|  | ed09cd7489 | ||
|  | eb3330d145 | ||
|  | 5ba0588c7b | ||
|  | d4a199f0e1 | ||
|  | 32dd186f4d | ||
|  | d820f55358 | ||
|  | 81f0fb3c74 | ||
|  | 972c83cd52 | ||
|  | 66a704af55 | ||
|  | 31c5d6e1c1 | ||
|  | bf0ab95c09 | ||
|  | c1d85f760d | ||
|  | 88ad2f4c18 | ||
|  | be9f9e7b0c | ||
|  | 2cc1973f62 | ||
|  | eb4625a0b9 | ||
|  | 5bfb01254b | ||
|  | bb80fa4a2d | ||
|  | ddb715d88d | ||
|  | 395b499856 | ||
|  | cce6a47f11 | ||
|  | 7fd17b4ec0 | ||
|  | e16ab2a0fd | ||
|  | 15f5364c30 | ||
|  | 65081767bf | ||
|  | c7c595e5fa | ||
|  | 5b24e8b69c | ||
|  | e6a845e606 | ||
|  | ec8b8a7b87 | ||
|  | 51a9205105 | ||
|  | ed5567fc73 | ||
|  | 4b3f5d74a0 | ||
|  | b01c5a05e7 | ||
|  | 36eddabc1c | ||
|  | ea11aa7a0d | ||
|  | e7efa76e6d | ||
|  | 41c8ca8ab4 | ||
|  | 4624079be7 | ||
|  | c6f6042271 | ||
|  | e9e3b9b7c6 | ||
|  | becbb09a29 | ||
|  | 6f6ab50995 | ||
|  | d8ee766860 | ||
|  | 108c26d8af | ||
|  | ed8d3088ca | ||
|  | 46c4e2d212 | ||
|  | 94891d45f9 | ||
|  | 7448ad109e | ||
|  | 6211dfe024 | ||
|  | 9b85200954 | ||
|  | 94ee739d91 | ||
|  | e81a6db9a3 | ||
|  | b2f5a259ab | ||
|  | c8a0d3c10d | ||
|  | 97df964051 | ||
|  | 66dd05f8bc | ||
|  | 19589d9117 | ||
|  | 8147b2e0b1 | ||
|  | be22f8cd14 | ||
|  | 868be9b7ff | ||
|  | 5011281104 | ||
|  | 42992c64ec | ||
|  | 2baff243ed | ||
|  | 83440a6b0f | ||
|  | 87c9a1c06c | ||
|  | b848fe249f | ||
|  | 1e804d97ce | ||
|  | 218d3c144b | ||
|  | 05a4905490 | ||
|  | 75103da378 | ||
|  | 9db9b53c81 | ||
|  | 0e4787f3e8 | ||
|  | f8d8d4b186 | ||
|  | 45e0a1ffea | ||
|  | 75c58093f1 | ||
|  | cc708e9fb4 | ||
|  | 2ce0e38827 | ||
|  | 5b980e8c13 | ||
|  | 21b602650c | ||
|  | fa4b7a1a69 | ||
|  | 977dfe700b | ||
|  | 48ac50e1c9 | ||
|  | 1a817947eb | ||
|  | be64603097 | ||
|  | f6b90c8271 | ||
|  | 26e4be87c7 | ||
|  | cddbb8d80d | ||
|  | 58023b4bf0 | ||
|  | 4f18a5f1c3 | ||
|  | 56df8d8bd3 | ||
|  | 211ec104c2 | ||
|  | 3fb573247d | ||
|  | 6aac44db14 | ||
|  | 3255e11cfc | ||
|  | 844bf29de1 | ||
|  | 04d91d1422 | ||
|  | db90e1f801 | ||
|  | 7f30748a41 | ||
|  | a4e0abb48f | ||
|  | 3f27dc89d8 | ||
|  | d6f6efc189 | ||
|  | 2cda49fc38 | ||
|  | 04f4a76b41 | ||
|  | 0a8f7085f3 | ||
|  | 7ae48d7390 | ||
|  | c908502644 | ||
|  | 2f0631809d | ||
|  | 91ab3bd972 | ||
|  | 672636313c | ||
|  | 79875ef50d | ||
|  | aea5445495 | ||
|  | 754a36fbc9 | ||
|  | 85dafc0b3c | ||
|  | b516ab9b4f | ||
|  | 1a27e60e55 | ||
|  | 2c710736e8 | ||
|  | 69b9ff69be | ||
|  | a3a4fc0cc2 | ||
|  | ae686bb15d | ||
|  | 68a5325849 | ||
|  | 75e3bddfa9 | ||
|  | bd3a8db438 | ||
|  | 102868bf74 | ||
|  | 1a73a27102 | ||
|  | a9cf34ab56 | ||
|  | 46d17c3314 | ||
|  | 40f816c311 | ||
|  | 13f1c12912 | ||
|  | 93c25f5d1b | ||
|  | aa6ec60c34 | ||
|  | ac159bb52e | ||
|  | 919aee64f9 | ||
|  | 553bec1a1f | ||
|  | bcb6d1cf93 | ||
|  | 7d24e5b279 | ||
|  | 12253e23b5 | ||
|  | 4acb66fb7a | ||
|  | 68ef85b64b | ||
|  | b73efe6bb4 | ||
|  | 89c84522d2 | ||
|  | 98172764ac | ||
|  | 448e881104 | ||
|  | f16134ab1f | ||
|  | f5dc1564a4 | ||
|  | 133df75bd4 | ||
|  | 440be0653a | ||
|  | d721a40ca5 | ||
|  | a9b252b8fa | ||
|  | 8a5b3ddee7 | ||
|  | d83e543a98 | ||
|  | bcd6e8fd63 | ||
|  | d5c5738aab | ||
|  | 9e4dfe081f | ||
|  | 090852b72b | ||
|  | ff5e038c49 | ||
|  | 5cc2e5f6e1 | ||
|  | 4e8c0573c4 | ||
|  | ce905ba2c4 | ||
|  | 3104c17fb3 | ||
|  | 7651941722 | ||
|  | 7bf938901a | ||
|  | f8b61d2926 | ||
|  | 4edea59ab1 | ||
|  | c8bcd2818d | ||
|  | 9b46dbaff1 | ||
|  | 17a139f27f | ||
|  | bd00c728d1 | ||
|  | 9d510b514c | ||
|  | 00dcc5ecda | ||
|  | dbbdd3f799 | ||
|  | 3541b4b968 | ||
|  | 5b1bf35a23 | ||
|  | 591b61945f | ||
|  | bd1943626b | ||
|  | f152cdef51 | ||
|  | 33f8c9747d | ||
|  | 714a5e26b3 | ||
|  | 7f2c6e40d3 | ||
|  | db676ec223 | ||
|  | ffb3e511a7 | ||
|  | e9e64f6a44 | ||
|  | a7b8adb0e1 | ||
|  | 4140ff03d7 | ||
|  | e042ef05a4 | ||
|  | 7c02e4d66a | ||
|  | 711794cfe1 | ||
|  | c0e4cf2358 | ||
|  | a92f8f36c1 | ||
|  | 12698dc347 | ||
|  | 3e6a55f78e | ||
|  | 7585f14b89 | ||
|  | 01b5fc4d49 | ||
|  | 0fb7d3bfc8 | ||
|  | 2cd74d355c | ||
|  | 3d405f8c63 | ||
|  | a92f0c4c6e | ||
|  | de142ac9d6 | ||
|  | 468ef7ecff | ||
|  | 4d768fd236 | ||
|  | bfc1f95190 | ||
|  | bc17ebd90e | ||
|  | bb1b3727cb | ||
|  | 4dbebefb45 | ||
|  | e1c5764fbf | ||
|  | 70f975e4f0 | ||
|  | 845567d1ba | ||
|  | f570447000 | ||
|  | 9d7b8f1f2f | ||
|  | bae6bfc32d | ||
|  | 8a63390464 | ||
|  | 0b52cd8b31 | ||
|  | f97569dd34 | ||
|  | a9164e63ab | ||
|  | 8c95067ec4 | ||
|  | 4f77bbeb2b | ||
|  | 8bbed2c831 | ||
|  | 6b43a23c4b | ||
|  | be9521f659 | ||
|  | 90761fd840 | ||
|  | d49d9a783c | ||
|  | d7dc7c4eda | ||
|  | fe64c6a841 | ||
|  | 2bbdc85a29 | ||
|  | 74628b7034 | ||
|  | 15aa249f64 | ||
|  | fdf58e1225 | ||
|  | 866f305686 | ||
|  | 1550e5343c | ||
|  | add3dd1077 | ||
|  | 79a142fb19 | ||
|  | 1a30fe4a1a | ||
|  | 4ff991764e | ||
|  | 001f066769 | ||
|  | c47b553a8e | ||
|  | 319af51f84 | ||
|  | 5dbaaae68e | ||
|  | 8c1a749a5a | ||
|  | fc8643f238 | ||
|  | c8653f19bf | ||
|  | b01100d818 | ||
|  | d4096a9026 | ||
|  | b9e780cdcd | ||
|  | b77cd56a01 | ||
|  | 9cdec156dc | ||
|  | 6aa5968863 | ||
|  | 8f7686cd7b | ||
|  | d8d384a979 | ||
|  | ade318bb78 | ||
|  | ed3aa8189f | ||
|  | 3e43597617 | ||
|  | 4c8e895ac7 | ||
|  | f6a3671366 | ||
|  | e641b0a965 | ||
|  | eddddc6c9b | ||
|  | f249d6306f | ||
|  | 5c31bd54e4 | ||
|  | 71ba73b38f | ||
|  | db0ff74857 | ||
|  | 1acb073737 | ||
|  | 251dda3652 | ||
|  | 22db24509d | ||
|  | 54c9d27fd8 | ||
|  | 01888ff078 | ||
|  | ffbd140a97 | ||
|  | dedf5c52d9 | ||
|  | 10465c5d68 | ||
|  | 1f4f64a7c0 | ||
|  | a6f116b57b | ||
|  | 0a80186a92 | ||
|  | 635bdf15cb | ||
|  | a72bdfdacc | ||
|  | dc3e04456c | ||
|  | b0e4fb7602 | ||
|  | df7aa3339b | ||
|  | c475536388 | ||
|  | cc7def89af | ||
|  | 58da87898e | ||
|  | bded5490d2 | ||
|  | c3715a2a3d | ||
|  | abf084f6c2 | ||
|  | 37ba409dc3 | ||
|  | f29488b24f | ||
|  | 71bdade7b9 | ||
|  | 60d97c887d | ||
|  | 5bba50f01f | ||
|  | 1f7884dc70 | ||
|  | 69dafd6c68 | ||
|  | 64b79cd5ac | ||
|  | 1af21735a9 | ||
|  | 9886af3cec | ||
|  | caa98b08da | ||
|  | 00caa13a12 | ||
|  | cfc0135e86 | ||
|  | 1d4dd4be96 | ||
|  | 676f790933 | ||
|  | 9f2a2b9869 | ||
|  | a0c09fc617 | ||
|  | 19d391fa05 | ||
|  | d1aa1fd4d8 | ||
|  | 4133f9c56f | ||
|  | 53055064e1 | ||
|  | 06090d8de1 | ||
|  | d6ccae38f8 | ||
|  | f7210effec | ||
|  | ea50ba16f9 | ||
|  | b62e4f6662 | ||
|  | 46af2e37a7 | ||
|  | 62f2a552ea | ||
|  | b053e02174 | ||
|  | 3798167908 | ||
|  | 56fe2014e1 | ||
|  | be2e64433f | ||
|  | 8732e89e55 | ||
|  | fdd0a93bad | ||
|  | dd12572b1d | ||
|  | e23f20227a | ||
|  | 5cc791690b | ||
|  | 93971537b4 | ||
|  | 87e816a7f5 | ||
|  | 0f45b1da48 | ||
|  | a20049c82a | ||
|  | 250005ad16 | ||
|  | 719aea2a58 | ||
|  | 68fef169f3 | ||
|  | c668201df4 | ||
|  | 1d68c8cc87 | ||
|  | b9ac8b42ea | ||
|  | b4a03a56b4 | ||
|  | e0c7269b8e | ||
|  | 73063df11b | ||
|  | f8855b83fa | ||
|  | 0d0459d83d | ||
|  | d2432716ea | ||
|  | 52ef85cba3 | ||
|  | 8140057bea | ||
|  | 22df59e229 | ||
|  | ed351eee54 | ||
|  | c021b4c368 | ||
|  | aac2a8f830 | ||
|  | 8269490dd1 | ||
|  | 82ced56bed | ||
|  | 982b8ea51d | ||
|  | 877c463494 | ||
|  | 9882582903 | ||
|  | ba566657f1 | ||
|  | cb1a178fbf | 
							
								
								
									
										35
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,35 +0,0 @@ | ||||
| <!-- | ||||
| ## 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: | ||||
| - [ ] running in Docker: | ||||
							
								
								
									
										39
									
								
								.github/ISSUE_TEMPLATE/--bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.github/ISSUE_TEMPLATE/--bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,39 +0,0 @@ | ||||
| --- | ||||
| name: Bug report | ||||
| about: Reproducible software issues in the core of Node-RED | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| <!-- | ||||
| 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. | ||||
|  | ||||
| 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: | ||||
							
								
								
									
										17
									
								
								.github/ISSUE_TEMPLATE/-anything-else.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/ISSUE_TEMPLATE/-anything-else.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,17 +0,0 @@ | ||||
| --- | ||||
| name: Anything Else | ||||
| about: Something that is not a bug report | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| Please DO NOT raise an issue. | ||||
|  | ||||
| We DO NOT use the issue tracker for general support or feature requests. Only bug reports should be raised here using the 'Bug report' template. | ||||
|  | ||||
| For general support, 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. | ||||
|  | ||||
| For feature requests, please use the Node-RED Forum](https://discourse.nodered.org). Many ideas have already been discussed there and you should search that for your request before starting a new discussion. | ||||
							
								
								
									
										61
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| name: 🐞 Report a bug | ||||
| description: File a bug/issue on the core of Node-RED | ||||
| labels: [needs-triage] | ||||
| body: | ||||
| - type: markdown | ||||
|   attributes: | ||||
|     value: | | ||||
|         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. | ||||
|  | ||||
|         To help us understand the issue, please fill-in as much of the following information as you can: | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Current Behavior | ||||
|     description: A clear & concise description of what you're experiencing. | ||||
|   validations: | ||||
|     required: false | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Expected Behavior | ||||
|     description: A clear & concise description of what you expected to happen. | ||||
|   validations: | ||||
|     required: false | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Steps To Reproduce | ||||
|     description: Steps to reproduce the behavior. | ||||
|   validations: | ||||
|     required: false | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Example flow | ||||
|     description: If you have a minimal example flow that demonstrates the issue, share it here. | ||||
|     value: | | ||||
|       ``` | ||||
|       paste your flow here | ||||
|       ``` | ||||
|   validations: | ||||
|     required: false | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Environment | ||||
|     description: Please tell us about your environment. Include any relevant information on how you are running Node-RED. | ||||
|     value: | | ||||
|         - Node-RED version: | ||||
|         - Node.js version: | ||||
|         - npm version: | ||||
|         - Platform/OS: | ||||
|         - Browser: | ||||
|   validations: | ||||
|     required: false | ||||
							
								
								
									
										14
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| blank_issues_enabled: true | ||||
| contact_links: | ||||
|   - name: ❓ Questions | ||||
|     url: https://discourse.nodered.org | ||||
|     about: Ask your question on the Node-RED forum | ||||
|   - name: ⭐️ Feature Request | ||||
|     url: https://discourse.nodered.org/c/development/feature-requests | ||||
|     about: Discuss your request with the community | ||||
|   - name: 🗂 Documentation | ||||
|     url: https://nodered.org/docs | ||||
|     about: Go straight to the documentation | ||||
|   - name: 💬 Slack | ||||
|     url: https://nodered.org/slack | ||||
|     about: Chat about the project on our slack team | ||||
| @@ -1,4 +1,4 @@ | ||||
| name: PublishDockerImage | ||||
| name: Publish Release | ||||
| env: | ||||
|   ACTIONS_ALLOW_UNSECURE_COMMANDS: true | ||||
| on: | ||||
| @@ -26,7 +26,7 @@ jobs: | ||||
|             path: 'node-red.github.io' | ||||
|       - uses: actions/setup-node@v1 | ||||
|         with: | ||||
|             node-version: '12' | ||||
|             node-version: '16' | ||||
|       - run: node ./node-red/.github/scripts/update-node-red-docker.js | ||||
|       - name: Create Docker Pull Request | ||||
|         uses: peter-evans/create-pull-request@v2 | ||||
							
								
								
									
										30
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| name: Run tests | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: [ master, dev ] | ||||
|   pull_request: | ||||
|     branches: [ master, dev ] | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|         node-version: [14, 16] | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - name: Use Node.js ${{ matrix.node-version }} | ||||
|       uses: actions/setup-node@v2 | ||||
|       with: | ||||
|         node-version: ${{ matrix.node-version }} | ||||
|     - name: Install Dependencies | ||||
|       run: npm install | ||||
|     - name: Run tests | ||||
|       run: | | ||||
|         npm run test | ||||
|     - name: Publish to coveralls.io | ||||
|       if: ${{ matrix.node-version == 14 }} | ||||
|       uses: coverallsapp/github-action@v1.1.2 | ||||
|       with: | ||||
|         github-token: ${{ github.token }} | ||||
							
								
								
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,21 +0,0 @@ | ||||
| sudo: false | ||||
| addons: | ||||
|   chrome: stable | ||||
| language: node_js | ||||
| matrix: | ||||
|   include: | ||||
|     - node_js: "14" | ||||
|       script: | ||||
|         - ./node_modules/.bin/grunt && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage | ||||
|         #    - scripts/install-ui-test-dependencies.sh && grunt test-ui | ||||
|       before_script: | ||||
|         - npm install -g coveralls | ||||
|     - node_js: "12" | ||||
|       script: | ||||
|         - ./node_modules/.bin/grunt no-coverage | ||||
|     - node_js: "10" | ||||
|       script: | ||||
|         - ./node_modules/.bin/grunt no-coverage | ||||
|     #- node_js: "8" | ||||
|     #  script: | ||||
|     #    - ./node_modules/.bin/grunt no-coverage | ||||
							
								
								
									
										14
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								API.md
									
									
									
									
									
								
							| @@ -1,8 +1,12 @@ | ||||
| Node-RED Modules | ||||
| --- | ||||
| Node-RED consists of 6 node modules under the `@node-red` scope, which are pulled together | ||||
| by the top-level `node-red` module. The typical scenario is where you are embedding Node-RED into your | ||||
| own application, in which case you would use the `node-red` module rather than any of the | ||||
| internal modules directly. | ||||
|  | ||||
| ```javascript | ||||
| let RED = require("node-red"); | ||||
| ``` | ||||
|  | ||||
| Node-RED provides a set of node modules that implement different parts of the | ||||
| application. | ||||
|  | ||||
| Module | Description | ||||
| -------|------- | ||||
| @@ -11,5 +15,5 @@ Module | Description | ||||
| [@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED | ||||
| [@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules | ||||
| [@node-red/registry](@node-red_registry.html) | the internal node registry | ||||
| @node-red/nodes | the default set of core nodes | ||||
| @node-red/nodes | the default set of core nodes. This module only contains the Node-RED nodes - it does not expose any APIs. | ||||
| @node-red/editor-client | the client-side resources of the Node-RED editor application | ||||
|   | ||||
							
								
								
									
										3039
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										3039
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -38,13 +38,18 @@ 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 [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 | ||||
| here: https://cla.js.foundation/node-red/node-red. | ||||
| All contributors need to sign the OpenJS Foundation's Contributor License Agreement. | ||||
| It is an online process and quick to do. If you raise a pull-request without | ||||
| having signed the CLA, you will be prompted to do so automatically. | ||||
|  | ||||
| If you raise a pull-request without having signed the CLA, you will be prompted | ||||
| to do so automatically. | ||||
|  | ||||
| ### Code Branches | ||||
|  | ||||
| When raising a PR for a fix or a new feature, it is important to target the right branch. | ||||
|  | ||||
|  - `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch. | ||||
|  - `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream. | ||||
|  - `dev` - this is the branch for new feature development targeting the next milestone release. | ||||
|  | ||||
| ### Coding standards | ||||
|  | ||||
|   | ||||
							
								
								
									
										56
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								Gruntfile.js
									
									
									
									
									
								
							| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| var path = require("path"); | ||||
| var fs = require("fs-extra"); | ||||
| var sass = require("node-sass"); | ||||
| var sass = require("sass"); | ||||
|  | ||||
| module.exports = function(grunt) { | ||||
|  | ||||
| @@ -40,11 +40,10 @@ module.exports = function(grunt) { | ||||
|     if (nonHeadless) { | ||||
|         process.env.NODE_RED_NON_HEADLESS = true; | ||||
|     } | ||||
|     let packageFile = grunt.file.readJSON('package.json') | ||||
|     process.env.NODE_RED_PACKAGE_VERSION = packageFile.version; | ||||
|  | ||||
|     const pkg = grunt.file.readJSON('package.json'); | ||||
|     process.env.NODE_RED_PACKAGE_VERSION = pkg.version; | ||||
|     grunt.initConfig({ | ||||
|         pkg: packageFile, | ||||
|         pkg: pkg, | ||||
|         paths: { | ||||
|             dist: ".dist" | ||||
|         }, | ||||
| @@ -138,6 +137,7 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/red.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/events.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/hooks.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/i18n.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/settings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/user.js", | ||||
| @@ -162,14 +162,16 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", | ||||
| @@ -181,7 +183,9 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editor.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tray.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js", | ||||
| @@ -197,7 +201,8 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js" | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js" | ||||
|                 ], | ||||
|                 dest: "packages/node_modules/@node-red/editor-client/public/red/red.js" | ||||
|             }, | ||||
| @@ -211,7 +216,9 @@ module.exports = function(grunt) { | ||||
|                         "node_modules/marked/marked.min.js", | ||||
|                         "node_modules/dompurify/dist/purify.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/i18next/i18next.min.js", | ||||
|                         "node_modules/i18next/i18next.min.js", | ||||
|                         "node_modules/i18next-http-backend/i18nextHttpBackend.min.js", | ||||
|                         "node_modules/jquery-i18next/jquery-i18next.min.js", | ||||
|                         "node_modules/jsonata/jsonata-es5.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js", | ||||
| @@ -284,7 +291,9 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/public/index.html", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/favicon.ico", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/icons", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor" | ||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node-red", | ||||
|                 ] | ||||
|             }, | ||||
|             release: { | ||||
| @@ -320,6 +329,12 @@ module.exports = function(grunt) { | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:keymaps','copy:build'] | ||||
|             }, | ||||
|             tours: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/tours/**/*.js' | ||||
|                 ], | ||||
|                 tasks: ['copy:build'] | ||||
|             }, | ||||
|             misc: { | ||||
|                 files: [ | ||||
|                     'CHANGELOG.md' | ||||
| @@ -374,11 +389,24 @@ module.exports = function(grunt) { | ||||
|                         src: [ | ||||
|                             'ace/**', | ||||
|                             'jquery/css/base/**', | ||||
|                             'font-awesome/**' | ||||
|                             'font-awesome/**', | ||||
|                             'monaco/dist/**', | ||||
|                             'monaco/types/extraLibs.js', | ||||
|                             'monaco/style.css', | ||||
|                             'monaco/monaco-bootstrap.js' | ||||
|                         ], | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src', | ||||
|                         src: [ | ||||
|                             'types/node/*.ts', | ||||
|                             'types/node-red/*.ts', | ||||
|                         ], | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/icons', | ||||
|                         src: '**', | ||||
| @@ -404,6 +432,12 @@ module.exports = function(grunt) { | ||||
|                         src: '**', | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/tours', | ||||
|                         src: '**', | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/' | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
| @@ -550,7 +584,7 @@ module.exports = function(grunt) { | ||||
|     grunt.registerMultiTask('attachCopyright', function() { | ||||
|         var files = this.data.src; | ||||
|         var copyright = "/**\n"+ | ||||
|             " * Copyright JS Foundation and other contributors, http://js.foundation\n"+ | ||||
|             " * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+ | ||||
|             " *\n"+ | ||||
|             " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+ | ||||
|             " * you may not use this file except in compliance with the License.\n"+ | ||||
|   | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -56,13 +56,13 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena | ||||
|  | ||||
| ## Authors | ||||
|  | ||||
| Node-RED is a project of the [OpenJS Foundation](https://openjsf.org). | ||||
| Node-RED is a project of the [OpenJS Foundation](http://openjsf.org). | ||||
|  | ||||
| It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/). | ||||
|  | ||||
| * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | ||||
| * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | ||||
| It is maintained by: | ||||
|  | ||||
|  * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | ||||
|  * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | ||||
|  * And many others... | ||||
|  | ||||
|  | ||||
| ## Copyright and license | ||||
|   | ||||
							
								
								
									
										116
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red", | ||||
|     "version": "1.3.4", | ||||
|     "version": "3.0.0-beta.2", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "http://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
| @@ -26,95 +26,101 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "ajv": "6.12.6", | ||||
|         "async-mutex": "0.3.1", | ||||
|         "acorn": "8.7.1", | ||||
|         "acorn-walk": "8.2.0", | ||||
|         "ajv": "8.11.0", | ||||
|         "async-mutex": "0.3.2", | ||||
|         "basic-auth": "2.0.1", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "body-parser": "1.19.0", | ||||
|         "cheerio": "0.22.0", | ||||
|         "body-parser": "1.20.0", | ||||
|         "cheerio": "1.0.0-rc.10", | ||||
|         "clone": "2.1.2", | ||||
|         "content-type": "1.0.4", | ||||
|         "cookie": "0.4.1", | ||||
|         "cookie-parser": "1.4.5", | ||||
|         "cookie": "0.5.0", | ||||
|         "cookie-parser": "1.4.6", | ||||
|         "cors": "2.8.5", | ||||
|         "cron": "1.7.2", | ||||
|         "denque": "1.5.0", | ||||
|         "express": "4.17.1", | ||||
|         "express-session": "1.17.1", | ||||
|         "fs-extra": "8.1.0", | ||||
|         "fs.notify": "0.0.4", | ||||
|         "cronosjs": "1.7.1", | ||||
|         "denque": "2.0.1", | ||||
|         "express": "4.18.1", | ||||
|         "express-session": "1.17.3", | ||||
|         "form-data": "4.0.0", | ||||
|         "fs-extra": "10.1.0", | ||||
|         "got": "11.8.3", | ||||
|         "hash-sum": "2.0.0", | ||||
|         "https-proxy-agent": "5.0.0", | ||||
|         "i18next": "15.1.2", | ||||
|         "iconv-lite": "0.6.2", | ||||
|         "hpagent": "1.0.0", | ||||
|         "https-proxy-agent": "5.0.1", | ||||
|         "i18next": "21.8.2", | ||||
|         "iconv-lite": "0.6.3", | ||||
|         "is-utf8": "0.2.1", | ||||
|         "js-yaml": "3.14.0", | ||||
|         "js-yaml": "4.1.0", | ||||
|         "json-stringify-safe": "5.0.1", | ||||
|         "jsonata": "1.8.4", | ||||
|         "jsonata": "1.8.6", | ||||
|         "lodash.clonedeep": "^4.5.0", | ||||
|         "media-typer": "1.1.0", | ||||
|         "memorystore": "1.6.6", | ||||
|         "mime": "2.5.2", | ||||
|         "moment-timezone": "0.5.33", | ||||
|         "mqtt": "4.2.6", | ||||
|         "multer": "1.4.2", | ||||
|         "memorystore": "1.6.7", | ||||
|         "mime": "3.0.0", | ||||
|         "moment": "2.29.3", | ||||
|         "moment-timezone": "0.5.34", | ||||
|         "mqtt": "4.3.7", | ||||
|         "multer": "1.4.4", | ||||
|         "mustache": "4.2.0", | ||||
|         "node-red-admin": "^0.2.6", | ||||
|         "node-red-node-rbe": "^0.5.0", | ||||
|         "node-red-node-sentiment": "^0.1.6", | ||||
|         "node-red-node-tail": "^0.3.0", | ||||
|         "node-red-admin": "^3.0.0", | ||||
|         "node-watch": "0.7.3", | ||||
|         "nopt": "5.0.0", | ||||
|         "oauth2orize": "1.11.0", | ||||
|         "oauth2orize": "1.11.1", | ||||
|         "on-headers": "1.0.2", | ||||
|         "passport": "0.4.1", | ||||
|         "passport": "0.5.2", | ||||
|         "passport-http-bearer": "1.0.1", | ||||
|         "passport-oauth2-client-password": "0.1.2", | ||||
|         "raw-body": "2.4.1", | ||||
|         "request": "2.88.0", | ||||
|         "semver": "6.3.0", | ||||
|         "tar": "6.1.0", | ||||
|         "uglify-js": "3.13.3", | ||||
|         "ws": "6.2.1", | ||||
|         "raw-body": "2.5.1", | ||||
|         "semver": "7.3.7", | ||||
|         "tar": "6.1.11", | ||||
|         "tough-cookie": "4.0.0", | ||||
|         "uglify-js": "3.15.5", | ||||
|         "uuid": "8.3.2", | ||||
|         "ws": "7.5.6", | ||||
|         "xml2js": "0.4.23" | ||||
|     }, | ||||
|     "optionalDependencies": { | ||||
|         "bcrypt": "3.0.8" | ||||
|         "bcrypt": "5.0.1" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "dompurify": "2.2.7", | ||||
|         "grunt": "1.3.0", | ||||
|         "dompurify": "2.3.8", | ||||
|         "grunt": "1.5.3", | ||||
|         "grunt-chmod": "~1.1.1", | ||||
|         "grunt-cli": "~1.4.2", | ||||
|         "grunt-cli": "~1.4.3", | ||||
|         "grunt-concurrent": "3.0.0", | ||||
|         "grunt-contrib-clean": "~2.0.0", | ||||
|         "grunt-contrib-compress": "1.6.0", | ||||
|         "grunt-contrib-concat": "~1.0.1", | ||||
|         "grunt-contrib-copy": "~1.0.0", | ||||
|         "grunt-contrib-jshint": "~2.1.0", | ||||
|         "grunt-contrib-uglify": "~4.0.1", | ||||
|         "grunt-contrib-watch": "~1.1.0", | ||||
|         "grunt-contrib-clean": "2.0.1", | ||||
|         "grunt-contrib-compress": "2.0.0", | ||||
|         "grunt-contrib-concat": "2.1.0", | ||||
|         "grunt-contrib-copy": "1.0.0", | ||||
|         "grunt-contrib-jshint": "3.2.0", | ||||
|         "grunt-contrib-uglify": "5.2.1", | ||||
|         "grunt-contrib-watch": "1.1.0", | ||||
|         "grunt-jsdoc": "2.4.1", | ||||
|         "grunt-jsdoc-to-markdown": "5.0.0", | ||||
|         "grunt-jsdoc-to-markdown": "6.0.0", | ||||
|         "grunt-jsonlint": "2.1.3", | ||||
|         "grunt-mkdir": "~1.1.0", | ||||
|         "grunt-npm-command": "~0.1.2", | ||||
|         "grunt-sass": "~3.1.0", | ||||
|         "grunt-simple-mocha": "~0.4.1", | ||||
|         "grunt-simple-nyc": "^3.0.1", | ||||
|         "http-proxy": "1.18.1", | ||||
|         "i18next-http-backend": "1.4.0", | ||||
|         "jquery-i18next": "1.2.1", | ||||
|         "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", | ||||
|         "marked": "2.0.1", | ||||
|         "marked": "4.0.15", | ||||
|         "minami": "1.2.3", | ||||
|         "mocha": "^5.2.0", | ||||
|         "mocha": "9.2.2", | ||||
|         "node-red-node-test-helper": "^0.2.7", | ||||
|         "node-sass": "^5.0.0", | ||||
|         "nodemon": "2.0.7", | ||||
|         "nodemon": "2.0.16", | ||||
|         "proxy": "^1.0.2", | ||||
|         "sass": "1.51.0", | ||||
|         "should": "13.2.3", | ||||
|         "sinon": "1.17.7", | ||||
|         "sinon": "11.1.2", | ||||
|         "stoppable": "^1.1.0", | ||||
|         "supertest": "5.0.0" | ||||
|         "supertest": "6.2.3" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=8" | ||||
|         "node": ">=14" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| Copyright JS Foundation and other contributors, http://js.foundation | ||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org/ | ||||
|  | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|   | ||||
							
								
								
									
										23
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| let runtimeAPI; | ||||
| let settings; | ||||
| const apiUtil = require("../util"); | ||||
| module.exports = { | ||||
|     init: function(_settings, _runtimeAPI) { | ||||
|         settings = _settings; | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|     }, | ||||
|     getReport: function(req, res) { | ||||
|         const diagnosticsOpts = settings.diagnostics || {}; | ||||
|         const opts = { | ||||
|             user: req.user, | ||||
|             scope: diagnosticsOpts.level || "basic" | ||||
|         } | ||||
|         if(diagnosticsOpts.enabled === false || diagnosticsOpts.enabled === "false") { | ||||
|             apiUtil.rejectHandler(req, res, {message: "diagnostics are disabled", status: 403, code: "diagnostics.disabled" }) | ||||
|         } else { | ||||
|             runtimeAPI.diagnostics.get(opts) | ||||
|             .then(function(result) { res.json(result); }) | ||||
|             .catch(err => apiUtil.rejectHandler(req, res, err)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ var context = require("./context"); | ||||
| var auth = require("../auth"); | ||||
| var info = require("./settings"); | ||||
| var plugins = require("./plugins"); | ||||
| var diagnostics = require("./diagnostics"); | ||||
|  | ||||
| var apiUtil = require("../util"); | ||||
|  | ||||
| @@ -34,11 +35,21 @@ module.exports = { | ||||
|         context.init(runtimeAPI); | ||||
|         info.init(settings,runtimeAPI); | ||||
|         plugins.init(runtimeAPI); | ||||
|         diagnostics.init(settings, runtimeAPI); | ||||
|  | ||||
|         var needsPermission = auth.needsPermission; | ||||
|  | ||||
|         var adminApp = express(); | ||||
|  | ||||
|         var defaultServerSettings = { | ||||
|             "x-powered-by": false | ||||
|         } | ||||
|         var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{}); | ||||
|         for (var eOption in serverSettings) { | ||||
|             adminApp.set(eOption, serverSettings[eOption]); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Flows | ||||
|         adminApp.get("/flows",needsPermission("flows.read"),flows.get,apiUtil.errorHandler); | ||||
|         adminApp.post("/flows",needsPermission("flows.write"),flows.post,apiUtil.errorHandler); | ||||
| @@ -86,6 +97,8 @@ module.exports = { | ||||
|         adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler); | ||||
|         adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler); | ||||
|  | ||||
|         adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler); | ||||
|  | ||||
|         return adminApp; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,9 +17,8 @@ module.exports = { | ||||
|             }) | ||||
|         } else { | ||||
|             opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages()); | ||||
|             if (/[^a-z\-\*]/i.test(opts.lang)) { | ||||
|                 res.json({}); | ||||
|                 return; | ||||
|             if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|                 opts.lang = "en-US"; | ||||
|             } | ||||
|             runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) { | ||||
|                 res.send(configs); | ||||
| @@ -32,9 +31,8 @@ module.exports = { | ||||
|             lang: req.query.lng, | ||||
|             req: apiUtils.getRequestLogObject(req) | ||||
|         } | ||||
|         if (/[^a-z\-\*]/i.test(opts.lang)) { | ||||
|             res.json({}); | ||||
|             return; | ||||
|         if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|             opts.lang = "en-US"; | ||||
|         } | ||||
|         runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) { | ||||
|             res.json(result); | ||||
|   | ||||
| @@ -106,9 +106,15 @@ async function login(req,res) { | ||||
|                 urlPrefix += "/"; | ||||
|             } | ||||
|             response = { | ||||
|                 "type":"strategy", | ||||
|                 "prompts":[{type:"button",label:mergedAdminAuth.strategy.label, url: urlPrefix + "auth/strategy"}] | ||||
|                 "type":"strategy" | ||||
|             } | ||||
|             if (mergedAdminAuth.strategy.autoLogin) { | ||||
|                 response.autoLogin = true | ||||
|                 response.loginRedirect = urlPrefix + "auth/strategy" | ||||
|             } | ||||
|             response.prompts = [ | ||||
|                 {type:"button",label:mergedAdminAuth.strategy.label, url: urlPrefix + "auth/strategy"} | ||||
|             ] | ||||
|             if (mergedAdminAuth.strategy.icon) { | ||||
|                 response.prompts[0].icon = mergedAdminAuth.strategy.icon; | ||||
|             } | ||||
| @@ -141,7 +147,7 @@ function completeVerify(profile,done) { | ||||
|     Users.authenticate(profile).then(function(user) { | ||||
|         if (user) { | ||||
|             Tokens.create(user.username,"node-red-editor",user.permissions).then(function(tokens) { | ||||
|                 log.audit({event: "auth.login",username:user.username,scope:user.permissions}); | ||||
|                 log.audit({event: "auth.login",user,username:user.username,scope:user.permissions}); | ||||
|                 user.tokens = tokens; | ||||
|                 done(null,user); | ||||
|             }); | ||||
| @@ -173,31 +179,38 @@ function genericStrategy(adminApp,strategy) { | ||||
|     adminApp.use(passport.session()); | ||||
|  | ||||
|     var options = strategy.options; | ||||
|     var verify = function() { | ||||
|         var originalDone = arguments[arguments.length-1]; | ||||
|         if (options.verify) { | ||||
|             var args = Array.from(arguments); | ||||
|             args[args.length-1] = function(err,profile) { | ||||
|                 if (err) { | ||||
|                     return originalDone(err); | ||||
|                 } else { | ||||
|                     return completeVerify(profile,originalDone); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|     passport.use(new strategy.strategy(options, | ||||
|         function() { | ||||
|             var originalDone = arguments[arguments.length-1]; | ||||
|             if (options.verify) { | ||||
|                 var args = Array.from(arguments); | ||||
|                 args[args.length-1] = function(err,profile) { | ||||
|                     if (err) { | ||||
|                         return originalDone(err); | ||||
|                     } else { | ||||
|                         return completeVerify(profile,originalDone); | ||||
|                     } | ||||
|                 }; | ||||
|                 options.verify.apply(null,args); | ||||
|             } else { | ||||
|                 var profile = arguments[arguments.length - 2]; | ||||
|                 return completeVerify(profile,originalDone); | ||||
|             } | ||||
|  | ||||
|             options.verify.apply(this,args); | ||||
|         } else { | ||||
|             var profile = arguments[arguments.length - 2]; | ||||
|             return completeVerify(profile,originalDone); | ||||
|         } | ||||
|     )); | ||||
|     }; | ||||
|     // Give our callback the same arity as the original one from options | ||||
|     if (options.verify) { | ||||
|         Object.defineProperty(verify, "length", { value: options.verify.length }) | ||||
|     } | ||||
|  | ||||
|     passport.use(new strategy.strategy(options, verify)); | ||||
|  | ||||
|     adminApp.get('/auth/strategy', | ||||
|         passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }), | ||||
|         completeGenerateStrategyAuth | ||||
|         passport.authenticate(strategy.name, {session:false, | ||||
|             failureMessage: true, | ||||
|             failureRedirect: settings.httpAdminRoot | ||||
|         }), | ||||
|         completeGenerateStrategyAuth, | ||||
|         handleStrategyError | ||||
|     ); | ||||
|  | ||||
|     var callbackMethodFunc = adminApp.get; | ||||
| @@ -205,8 +218,13 @@ function genericStrategy(adminApp,strategy) { | ||||
|         callbackMethodFunc = adminApp.post; | ||||
|     } | ||||
|     callbackMethodFunc.call(adminApp,'/auth/strategy/callback', | ||||
|         passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }), | ||||
|         completeGenerateStrategyAuth | ||||
|         passport.authenticate(strategy.name, { | ||||
|             session:false, | ||||
|             failureMessage: true, | ||||
|             failureRedirect: settings.httpAdminRoot | ||||
|         }), | ||||
|         completeGenerateStrategyAuth, | ||||
|         handleStrategyError | ||||
|     ); | ||||
|  | ||||
| } | ||||
| @@ -216,6 +234,13 @@ function completeGenerateStrategyAuth(req,res) { | ||||
|     // Successful authentication, redirect home. | ||||
|     res.redirect(settings.httpAdminRoot + '?access_token='+tokens.accessToken); | ||||
| } | ||||
| function handleStrategyError(err, req, res, next) { | ||||
|     if (res.headersSent) { | ||||
|         return next(err) | ||||
|     } | ||||
|     log.audit({event: "auth.login.fail.oauth",error:err.toString()}); | ||||
|     res.redirect(settings.httpAdminRoot + '?session_message='+err.toString()); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     init: init, | ||||
|   | ||||
| @@ -92,10 +92,16 @@ var passwordTokenExchange = function(client, username, password, scope, done) { | ||||
|                 loginAttempts = loginAttempts.filter(function(logEntry) { | ||||
|                     return logEntry.user !== username; | ||||
|                 }); | ||||
|                 Tokens.create(username,client.id,scope).then(function(tokens) { | ||||
|                     log.audit({event: "auth.login",username:username,client:client.id,scope:scope}); | ||||
|                     done(null,tokens.accessToken,null,{expires_in:tokens.expires_in}); | ||||
|                 }); | ||||
|                 // Check if the user contains a user defined token and use it | ||||
|                 // instead of generating a new token | ||||
|                 if(user.token){ | ||||
|                     done(null,user.token,null,null); | ||||
|                 } else { | ||||
|                     Tokens.create(username,client.id,scope).then(function(tokens) { | ||||
|                         log.audit({event: "auth.login",user,username:username,client:client.id,scope:scope}); | ||||
|                         done(null,tokens.accessToken,null,{expires_in:tokens.expires_in}); | ||||
|                     }); | ||||
|                 } | ||||
|             } else { | ||||
|                 log.audit({event: "auth.login.fail.permissions",username:username,client:client.id,scope:scope}); | ||||
|                 done(null,false); | ||||
| @@ -146,7 +152,7 @@ function authenticateUserToken(req) { | ||||
|                 } else { | ||||
|                     reject(); | ||||
|                 } | ||||
|             }); | ||||
|             }).catch(reject); | ||||
|         } else { | ||||
|             reject(); | ||||
|         } | ||||
| @@ -163,6 +169,9 @@ TokensStrategy.prototype.authenticate = function(req) { | ||||
|     authenticateUserToken(req).then(user => { | ||||
|         this.success(user,{scope:user.permissions}); | ||||
|     }).catch(err => { | ||||
|         if (err) { | ||||
|             log.trace("token authentication failure: "+err.stack?err.stack:err) | ||||
|         } | ||||
|         this.fail(401); | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -158,25 +158,31 @@ function CommsConnection(ws, user) { | ||||
| } | ||||
|  | ||||
| CommsConnection.prototype.send = function(topic,data) { | ||||
|     var self = this; | ||||
|     if (topic && data) { | ||||
|         this.stack.push({topic:topic,data:data}); | ||||
|     } | ||||
|     this._queueSend(); | ||||
| } | ||||
| CommsConnection.prototype._queueSend = function() { | ||||
|     var self = this; | ||||
|     if (!this._xmitTimer) { | ||||
|         this._xmitTimer = setTimeout(function() { | ||||
|             try { | ||||
|                 self.ws.send(JSON.stringify(self.stack)); | ||||
|                 self.ws.send(JSON.stringify(self.stack.splice(0,50))); | ||||
|                 self.lastSentTime = Date.now(); | ||||
|             } catch(err) { | ||||
|                 removeActiveConnection(self); | ||||
|                 log.warn(log._("comms.error-send",{message:err.toString()})); | ||||
|             } | ||||
|             delete self._xmitTimer; | ||||
|             self.stack = []; | ||||
|             if (self.stack.length > 0) { | ||||
|                 self._queueSend(); | ||||
|             } | ||||
|         },50); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| CommsConnection.prototype.subscribe = function(topic) { | ||||
|     runtimeAPI.comms.subscribe({ | ||||
|         user: this.user, | ||||
|   | ||||
| @@ -64,10 +64,12 @@ module.exports = { | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|             if (settings.httpServerOptions) { | ||||
|                 for (var eOption in settings.httpServerOptions) { | ||||
|                     editorApp.set(eOption, settings.httpServerOptions[eOption]); | ||||
|                 } | ||||
|             var defaultServerSettings = { | ||||
|                 "x-powered-by": false | ||||
|             } | ||||
|             var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{}); | ||||
|             for (var eOption in serverSettings) { | ||||
|                 editorApp.set(eOption, serverSettings[eOption]); | ||||
|             } | ||||
|             editorApp.get("/",ensureRuntimeStarted,ui.ensureSlash,ui.editor); | ||||
|  | ||||
|   | ||||
| @@ -48,9 +48,10 @@ module.exports = { | ||||
|         var prevLang = i18n.i.language; | ||||
|         // Trigger a load from disk of the language if it is not the default | ||||
|         i18n.i.changeLanguage(lang, function(){ | ||||
|             var catalog = loadResource(lang, namespace); | ||||
|             res.json(catalog||{}); | ||||
|             i18n.i.changeLanguage(prevLang, function() { | ||||
|                 var catalog = loadResource(lang, namespace); | ||||
|                 res.json(catalog||{}); | ||||
|             }); | ||||
|         }); | ||||
|         i18n.i.changeLanguage(prevLang); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -122,6 +122,7 @@ module.exports = { | ||||
|             } | ||||
|  | ||||
|             if (req.body.active) { | ||||
|                 opts.clearContext = req.body.hasOwnProperty('clearContext')?req.body.clearContext:true | ||||
|                 runtimeAPI.projects.setActiveProject(opts).then(function() { | ||||
|                     listProjects(req,res); | ||||
|                 }).catch(function(err) { | ||||
|   | ||||
| @@ -18,14 +18,6 @@ var apiUtils = require("../util"); | ||||
| var express = require("express"); | ||||
| var runtimeAPI; | ||||
|  | ||||
| function getUsername(userObj) { | ||||
|     var username = '__default'; | ||||
|     if ( userObj && userObj.name ) { | ||||
|         username = userObj.name; | ||||
|     } | ||||
|     return username; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     init: function(_runtimeAPI) { | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|   | ||||
| @@ -24,16 +24,20 @@ var defaultContext = { | ||||
|     page: { | ||||
|         title: "Node-RED", | ||||
|         favicon: "favicon.ico", | ||||
|         tabicon: "red/images/node-red-icon-black.svg" | ||||
|         tabicon: { | ||||
|             icon: "red/images/node-red-icon-black.svg", | ||||
|             colour: "#8f0000" | ||||
|         }, | ||||
|         version: require(path.join(__dirname,"../../package.json")).version | ||||
|     }, | ||||
|     header: { | ||||
|         title: "Node-RED", | ||||
|         image: "red/images/node-red.svg" | ||||
|     }, | ||||
|     asset: { | ||||
|         red: (process.env.NODE_ENV == "development")? "red/red.js":"red/red.min.js", | ||||
|         main: (process.env.NODE_ENV == "development")? "red/main.js":"red/main.min.js", | ||||
|  | ||||
|         red: "red/red.min.js", | ||||
|         main: "red/main.min.js", | ||||
|         vendorMonaco: "" | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @@ -74,7 +78,7 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) { | ||||
|             let fullPath = array[i]; | ||||
|             if (baseDirectory) { | ||||
|                 fullPath = path.resolve(baseDirectory,array[i]); | ||||
|                 if (fullPath.indexOf(baseDirectory) !== 0) { | ||||
|                 if (fullPath.indexOf(path.resolve(baseDirectory)) !== 0) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -91,8 +95,13 @@ module.exports = { | ||||
|     init: function(settings, _runtimeAPI) { | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|         themeContext = clone(defaultContext); | ||||
|         if (process.env.NODE_ENV == "development") { | ||||
|             themeContext.asset.red = "red/red.js"; | ||||
|             themeContext.asset.main = "red/main.js"; | ||||
|         } | ||||
|         themeSettings = null; | ||||
|         theme = settings.editorTheme || {}; | ||||
|         themeContext.asset.vendorMonaco = ((theme.codeEditor || {}).lib === "monaco") ? "vendor/monaco/monaco-bootstrap.js" : ""; | ||||
|         activeTheme = theme.theme; | ||||
|     }, | ||||
|  | ||||
| @@ -122,9 +131,13 @@ module.exports = { | ||||
|             } | ||||
|  | ||||
|             if (theme.page.tabicon) { | ||||
|                 url = serveFile(themeApp,"/tabicon/",theme.page.tabicon) | ||||
|                 let icon = theme.page.tabicon.icon || theme.page.tabicon | ||||
|                 url = serveFile(themeApp,"/tabicon/", icon) | ||||
|                 if (url) { | ||||
|                     themeContext.page.tabicon = url; | ||||
|                     themeContext.page.tabicon.icon = url; | ||||
|                 } | ||||
|                 if (theme.page.tabicon.colour) { | ||||
|                     themeContext.page.tabicon.colour = theme.page.tabicon.colour | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -215,6 +228,11 @@ module.exports = { | ||||
|         if (theme.theme) { | ||||
|             themeSettings.theme = theme.theme; | ||||
|         } | ||||
|  | ||||
|         if (theme.hasOwnProperty("tours")) { | ||||
|             themeSettings.tours = theme.tours; | ||||
|         } | ||||
|  | ||||
|         return themeApp; | ||||
|     }, | ||||
|     context: async function() { | ||||
| @@ -231,6 +249,7 @@ module.exports = { | ||||
|                         themePlugin.path | ||||
|                     ); | ||||
|                     themeContext.page.css = cssFiles.concat(themeContext.page.css || []) | ||||
|                     theme.page = theme.page || {_:{}} | ||||
|                     theme.page._.css = cssFiles.concat(theme.page._.css || []) | ||||
|                 } | ||||
|                 if (themePlugin.scripts) { | ||||
| @@ -241,7 +260,11 @@ module.exports = { | ||||
|                         themePlugin.path | ||||
|                     ) | ||||
|                     themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || []) | ||||
|                     theme.page._.scripts = cssFiles.concat(theme.page._.scripts || []) | ||||
|                     theme.page = theme.page || {_:{}} | ||||
|                     theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || []) | ||||
|                 } | ||||
|                 if(theme.codeEditor) { | ||||
|                     theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options); | ||||
|                 } | ||||
|             } | ||||
|             activeThemeInitialised = true; | ||||
|   | ||||
| @@ -91,7 +91,16 @@ module.exports = { | ||||
|     }, | ||||
|  | ||||
|     editor: async function(req,res) { | ||||
|         res.send(Mustache.render(editorTemplate,await theme.context())); | ||||
|  | ||||
|         let sessionMessages; | ||||
|         if (req.session && req.session.messages) { | ||||
|             sessionMessages = JSON.stringify(req.session.messages); | ||||
|             delete req.session.messages | ||||
|         } | ||||
|         res.send(Mustache.render(editorTemplate,{ | ||||
|             sessionMessages, | ||||
|             ...await theme.context() | ||||
|         })); | ||||
|     }, | ||||
|     editorResources: express.static(path.join(editorClientDir,'public')) | ||||
| }; | ||||
|   | ||||
| @@ -64,6 +64,14 @@ function init(settings,_server,storage,runtimeAPI) { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var defaultServerSettings = { | ||||
|             "x-powered-by": false | ||||
|         } | ||||
|         var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{}); | ||||
|         for (var eOption in serverSettings) { | ||||
|             adminApp.set(eOption, serverSettings[eOption]); | ||||
|         } | ||||
|  | ||||
|         auth.init(settings,storage); | ||||
|  | ||||
|         var maxApiRequestSize = settings.apiMaxLength || '5mb'; | ||||
| @@ -82,6 +90,8 @@ function init(settings,_server,storage,runtimeAPI) { | ||||
|                     auth.getToken, | ||||
|                     auth.errorHandler | ||||
|                 ); | ||||
|             } else if (settings.adminAuth.tokens) { | ||||
|                 adminApp.use(passport.initialize()); | ||||
|             } | ||||
|             adminApp.post("/auth/revoke",auth.needsPermission(""),auth.revoke,apiUtil.errorHandler); | ||||
|         } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-api", | ||||
|     "version": "1.3.4", | ||||
|     "version": "3.0.0-beta.2", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,25 +16,25 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "1.3.4", | ||||
|         "@node-red/editor-client": "1.3.4", | ||||
|         "@node-red/util": "3.0.0-beta.2", | ||||
|         "@node-red/editor-client": "3.0.0-beta.2", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "body-parser": "1.19.0", | ||||
|         "body-parser": "1.20.0", | ||||
|         "clone": "2.1.2", | ||||
|         "cors": "2.8.5", | ||||
|         "express-session": "1.17.1", | ||||
|         "express": "4.17.1", | ||||
|         "memorystore": "1.6.6", | ||||
|         "mime": "2.5.2", | ||||
|         "multer": "1.4.2", | ||||
|         "express-session": "1.17.3", | ||||
|         "express": "4.18.1", | ||||
|         "memorystore": "1.6.7", | ||||
|         "mime": "3.0.0", | ||||
|         "multer": "1.4.4", | ||||
|         "mustache": "4.2.0", | ||||
|         "oauth2orize": "1.11.0", | ||||
|         "oauth2orize": "1.11.1", | ||||
|         "passport-http-bearer": "1.0.1", | ||||
|         "passport-oauth2-client-password": "0.1.2", | ||||
|         "passport": "0.4.1", | ||||
|         "ws": "6.2.1" | ||||
|         "passport": "0.5.2", | ||||
|         "ws": "7.5.6" | ||||
|     }, | ||||
|     "optionalDependencies": { | ||||
|         "bcrypt": "3.0.6" | ||||
|         "bcrypt": "5.0.1" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| Copyright JS Foundation and other contributors, http://js.foundation | ||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org/ | ||||
|  | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|   | ||||
| @@ -185,7 +185,8 @@ | ||||
|             "create-default-package": "Standardpaketdatei erstellen", | ||||
|             "no-thanks": "Nein, Danke", | ||||
|             "create-default-project": "Standardprojektdateien erstellen", | ||||
|             "show-merge-conflicts": "Merge-Konflikte anzeigen" | ||||
|             "show-merge-conflicts": "Merge-Konflikte anzeigen", | ||||
|             "unknownNodesButton": "Finden Sie unbekannte nodes" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -269,7 +270,9 @@ | ||||
|         "successfulRestart": "Flows erfolgreich neugestartet", | ||||
|         "deployFailed": "Übernahme (deploy) fehlgeschlagen: __message__", | ||||
|         "unusedConfigNodes": "Einige Konfigurations-Nodes werden nicht verwendet.", | ||||
|         "unusedConfigNodesLink": "Hier klicken, um sie anzuschauen.", | ||||
|         "unusedConfigNodesButton":"Finden Sie ungenutzte konfig nodes", | ||||
|         "unknownNodesButton":"Finden Sie unbekannte nodes", | ||||
|         "invalidNodesButton":"Finden Sie ungültige nodes", | ||||
|         "errors": { | ||||
|             "noResponse": "Keine Antwort vom Server" | ||||
|         }, | ||||
| @@ -634,14 +637,7 @@ | ||||
|             "empty": "leer", | ||||
|             "globalConfig": "Globale Konfigurations-Nodes", | ||||
|             "triggerAction": "Auslösen", | ||||
|             "find": "Suche im Arbeitsbereich", | ||||
|             "search": { | ||||
|                 "configNodes": "Konfigurations-Nodes", | ||||
|                 "unusedConfigNodes": "Unbenutzte Konfigurations-Nodes", | ||||
|                 "invalidNodes": "Ungültige Nodes", | ||||
|                 "uknownNodes": "Unbekannte Nodes", | ||||
|                 "unusedSubflows": "Unbenutzte Subflows" | ||||
|             } | ||||
|             "find": "Suche im Arbeitsbereich" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "Hilfe", | ||||
| @@ -863,7 +859,14 @@ | ||||
|     }, | ||||
|     "search": { | ||||
|         "empty": "Keine Übereinstimmungen gefunden", | ||||
|         "addNode": "Node hinzufügen ..." | ||||
|         "addNode": "Node hinzufügen ...", | ||||
|         "options": { | ||||
|             "configNodes": "Konfigurations-Nodes", | ||||
|             "unusedConfigNodes": "Unbenutzte Konfigurations-Nodes", | ||||
|             "invalidNodes": "Ungültige Nodes", | ||||
|             "uknownNodes": "Unbekannte Nodes", | ||||
|             "unusedSubflows": "Unbenutzte Subflows" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "Funktionen", | ||||
|   | ||||
| @@ -53,8 +53,17 @@ | ||||
|         "confirmDelete": "Confirm delete", | ||||
|         "delete": "Are you sure you want to delete '__label__'?", | ||||
|         "dropFlowHere": "Drop the flow here", | ||||
|         "addFlow": "Add Flow", | ||||
|         "listFlows": "List Flows", | ||||
|         "addFlow": "Add flow", | ||||
|         "addFlowToRight": "Add flow to the right", | ||||
|         "hideFlow": "Hide flow", | ||||
|         "hideOtherFlows": "Hide other flows", | ||||
|         "showAllFlows": "Show all flows", | ||||
|         "hideAllFlows": "Hide all flows", | ||||
|         "hiddenFlows": "List __count__ hidden flow", | ||||
|         "hiddenFlows_plural": "List __count__ hidden flows", | ||||
|         "showLastHiddenFlow": "Show last hidden flow", | ||||
|         "listFlows": "List flows", | ||||
|         "listSubflows": "List subflows", | ||||
|         "status": "Status", | ||||
|         "enabled": "Enabled", | ||||
|         "disabled":"Disabled", | ||||
| @@ -66,6 +75,8 @@ | ||||
|             "view": { | ||||
|                 "view": "View", | ||||
|                 "grid": "Grid", | ||||
|                 "storeZoom": "Restore zoom level on load", | ||||
|                 "storePosition": "Restore scroll position on load", | ||||
|                 "showGrid": "Show grid", | ||||
|                 "snapGrid": "Snap to grid", | ||||
|                 "gridSize": "Grid size", | ||||
| @@ -83,6 +94,7 @@ | ||||
|             "palette": { | ||||
|                 "show": "Show palette" | ||||
|             }, | ||||
|             "edit": "Edit", | ||||
|             "settings": "Settings", | ||||
|             "userSettings": "User Settings", | ||||
|             "nodes": "Nodes", | ||||
| @@ -105,24 +117,43 @@ | ||||
|             "editPalette":"Manage palette", | ||||
|             "other": "Other", | ||||
|             "showTips": "Show tips", | ||||
|             "showWelcomeTours": "Show guided tours for new versions", | ||||
|             "help": "Node-RED website", | ||||
|             "projects": "Projects", | ||||
|             "projects-new": "New", | ||||
|             "projects-open": "Open", | ||||
|             "projects-settings": "Project Settings", | ||||
|             "showNodeLabelDefault": "Show label of newly added nodes", | ||||
|             "codeEditor": "Code Editor", | ||||
|             "groups": "Groups", | ||||
|             "groupSelection": "Group selection", | ||||
|             "ungroupSelection": "Ungroup selection", | ||||
|             "groupMergeSelection": "Merge selection", | ||||
|             "groupRemoveSelection": "Remove from group" | ||||
|             "groupRemoveSelection": "Remove from group", | ||||
|             "arrange":"Arrange", | ||||
|             "alignLeft":"Align to left", | ||||
|             "alignCenter":"Align to center", | ||||
|             "alignRight":"Align to right", | ||||
|             "alignTop":"Align to top", | ||||
|             "alignMiddle":"Align to middle", | ||||
|             "alignBottom":"Align to bottom", | ||||
|             "distributeHorizontally":"Distribute horizontally", | ||||
|             "distributeVertically":"Distribute vertically", | ||||
|             "moveToBack":"Move to back", | ||||
|             "moveToFront":"Move to front", | ||||
|             "moveBackwards":"Move backwards", | ||||
|             "moveForwards":"Move forwards" | ||||
|         } | ||||
|     }, | ||||
|     "actions": { | ||||
|         "toggle-navigator": "Toggle navigator", | ||||
|         "zoom-out": "Zoom out", | ||||
|         "zoom-reset": "Reset zoom", | ||||
|         "zoom-in": "Zoom in" | ||||
|         "zoom-in": "Zoom in", | ||||
|         "search-flows": "Search flows", | ||||
|         "search-prev": "Previous", | ||||
|         "search-next": "Next", | ||||
|         "search-counter": "\"__term__\" __result__ of __count__" | ||||
|     }, | ||||
|     "user": { | ||||
|         "loggedInAs": "Logged in as __name__", | ||||
| @@ -186,7 +217,8 @@ | ||||
|             "create-default-package": "Create default package file", | ||||
|             "no-thanks": "No thanks", | ||||
|             "create-default-project": "Create default project files", | ||||
|             "show-merge-conflicts": "Show merge conflicts" | ||||
|             "show-merge-conflicts": "Show merge conflicts", | ||||
|             "unknownNodesButton": "Search for unknown nodes" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -270,7 +302,9 @@ | ||||
|         "successfulRestart": "Successfully restarted flows", | ||||
|         "deployFailed": "Deploy failed: __message__", | ||||
|         "unusedConfigNodes":"You have some unused configuration nodes.", | ||||
|         "unusedConfigNodesLink":"Click here to see them", | ||||
|         "unusedConfigNodesButton":"Search unused config nodes", | ||||
|         "unknownNodesButton":"Search for unknown nodes", | ||||
|         "invalidNodesButton":"Search for invalid nodes", | ||||
|         "errors": { | ||||
|             "noResponse": "no response from server" | ||||
|         }, | ||||
| @@ -449,8 +483,9 @@ | ||||
|         "unassigned": "Unassigned", | ||||
|         "global": "global", | ||||
|         "workspace": "workspace", | ||||
|         "selectAll": "Select all nodes", | ||||
|         "selectAllConnected": "Select all connected nodes", | ||||
|         "selectAll": "Select all", | ||||
|         "selectNone": "Select none", | ||||
|         "selectAllConnected": "Select connected", | ||||
|         "addRemoveNode": "Add/remove node from selection", | ||||
|         "editSelected": "Edit selected node", | ||||
|         "deleteSelected": "Delete selected nodes or link", | ||||
| @@ -463,10 +498,14 @@ | ||||
|         "copyNode": "Copy selected nodes", | ||||
|         "cutNode": "Cut selected nodes", | ||||
|         "pasteNode": "Paste nodes", | ||||
|         "undoChange": "Undo the last change performed", | ||||
|         "copyGroupStyle": "Copy group style", | ||||
|         "pasteGroupStyle": "Paste group style", | ||||
|         "undoChange": "Undo", | ||||
|         "redoChange": "Redo", | ||||
|         "searchBox": "Open search box", | ||||
|         "managePalette": "Manage palette", | ||||
|         "actionList":"Action list" | ||||
|         "actionList": "Action list", | ||||
|         "splitWireWithLinks": "Split selection with Link nodes" | ||||
|     }, | ||||
|     "library": { | ||||
|         "library": "Library", | ||||
| @@ -518,7 +557,8 @@ | ||||
|             "nodeEnabled_plural": "Nodes enabled:", | ||||
|             "nodeDisabled": "Node disabled:", | ||||
|             "nodeDisabled_plural": "Nodes disabled:", | ||||
|             "nodeUpgraded": "Node module __module__ upgraded to version __version__" | ||||
|             "nodeUpgraded": "Node module __module__ upgraded to version __version__", | ||||
|             "unknownNodeRegistered": "Error loading node: <ul><li>__type__<br>__error__</li></ul>" | ||||
|         }, | ||||
|         "editor": { | ||||
|             "title": "Manage palette", | ||||
| @@ -635,14 +675,7 @@ | ||||
|             "empty": "empty", | ||||
|             "globalConfig": "Global Configuration Nodes", | ||||
|             "triggerAction": "Trigger action", | ||||
|             "find": "Find in workspace", | ||||
|             "search": { | ||||
|                 "configNodes": "Configuration nodes", | ||||
|                 "unusedConfigNodes": "Unused configuration nodes", | ||||
|                 "invalidNodes": "Invalid nodes", | ||||
|                 "uknownNodes": "Unknown nodes", | ||||
|                 "unusedSubflows": "Unused subflows" | ||||
|             } | ||||
|             "find": "Find in workspace" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "Help", | ||||
| @@ -863,8 +896,20 @@ | ||||
|         "addTitle": "add an item" | ||||
|     }, | ||||
|     "search": { | ||||
|         "history": "Search history", | ||||
|         "clear": "clear all", | ||||
|         "empty": "No matches found", | ||||
|         "addNode": "add a node..." | ||||
|         "addNode": "add a node...", | ||||
|         "options": { | ||||
|             "configNodes": "Configuration nodes", | ||||
|             "unusedConfigNodes": "Unused configuration nodes", | ||||
|             "invalidNodes": "Invalid nodes", | ||||
|             "uknownNodes": "Unknown nodes", | ||||
|             "unusedSubflows": "Unused subflows", | ||||
|             "hiddenFlows": "Hidden flows", | ||||
|             "modifiedNodes": "Modified nodes and flows", | ||||
|             "thisFlow": "Current flow" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "Functions", | ||||
| @@ -885,6 +930,9 @@ | ||||
|             "eval": "Error evaluating expression:\n  __message__" | ||||
|         } | ||||
|     }, | ||||
|     "monaco": { | ||||
|         "setTheme": "Set theme" | ||||
|     }, | ||||
|     "jsEditor": { | ||||
|         "title": "JavaScript editor" | ||||
|     }, | ||||
| @@ -896,6 +944,8 @@ | ||||
|         "format": "format JSON", | ||||
|         "rawMode": "Edit JSON", | ||||
|         "uiMode": "Visual editor", | ||||
|         "rawMode-readonly": "JSON", | ||||
|         "uiMode-readonly": "Visual", | ||||
|         "insertAbove": "Insert above", | ||||
|         "insertBelow": "Insert below", | ||||
|         "addItem": "Add item", | ||||
| @@ -1057,7 +1107,8 @@ | ||||
|             "not-git": "Not a git repository", | ||||
|             "no-resource": "Repository not found", | ||||
|             "cant-get-ssh-key-path": "Error! Can't get selected SSH key path.", | ||||
|             "unexpected_error": "unexpected_error" | ||||
|             "unexpected_error": "unexpected_error", | ||||
|             "clearContext": "Clear context when switching projects" | ||||
|         }, | ||||
|         "delete": { | ||||
|             "confirm": "Are you sure you want to delete this project?" | ||||
| @@ -1104,6 +1155,14 @@ | ||||
|         "preview": "UI Preview", | ||||
|         "defaultValue": "Default value" | ||||
|     }, | ||||
|     "tourGuide": { | ||||
|         "takeATour": "Take a tour", | ||||
|         "start": "Start", | ||||
|         "next": "Next" | ||||
|     }, | ||||
|     "diagnostics": { | ||||
|         "title": "System Info" | ||||
|     }, | ||||
|     "languages" : { | ||||
|         "de": "German", | ||||
|         "en-US": "English", | ||||
| @@ -1112,5 +1171,21 @@ | ||||
|         "ru": "Russian", | ||||
|         "zh-CN": "Chinese(Simplified)", | ||||
|         "zh-TW": "Chinese(Traditional)" | ||||
|     }, | ||||
|     "validator": { | ||||
|         "errors": { | ||||
| 	    "invalid-json": "Invalid JSON data: __error__", | ||||
| 	    "invalid-json-prop": "__prop__: invalid JSON data: __error__", | ||||
| 	    "invalid-prop": "Invalid property expression", | ||||
| 	    "invalid-prop-prop": "__prop__: invalid property expression", | ||||
| 	    "invalid-num": "Invalid number", | ||||
| 	    "invalid-num-prop": "__prop__: invalid number", | ||||
| 	    "invalid-regexp": "Invalid input pattern", | ||||
| 	    "invalid-regex-prop": "__prop__: invalid input pattern", | ||||
| 	    "missing-required-prop": "__prop__: property value missing", | ||||
| 	    "invalid-config": "__prop__: invalid configuration node", | ||||
| 	    "missing-config": "__prop__: missing configuration node", | ||||
| 	    "validation-error": "__prop__: validation error: __node__, __id__: __error__" | ||||
| 	} | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -52,8 +52,8 @@ | ||||
|         "desc": "Finds occurrences of `pattern` within `str` and replaces them with `replacement`.\n\nThe optional `limit` parameter is the maximum number of replacements." | ||||
|     }, | ||||
|     "$now": { | ||||
|         "args":"", | ||||
|         "desc":"Generates a timestamp in ISO 8601 compatible format and returns it as a string." | ||||
|         "args":"$[picture [, timezone]]", | ||||
|         "desc":"Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional picture and timezone parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function" | ||||
|     }, | ||||
|     "$base64encode": { | ||||
|         "args":"string", | ||||
| @@ -200,8 +200,8 @@ | ||||
|         "desc": "Returns a copy of the `string` with extra padding, if necessary, so that its total number of characters is at least the absolute value of the `width` parameter.\n\nIf `width` is a positive number, then the string is padded to the right; if negative, it is padded to the left.\n\nThe optional `char` argument specifies the padding character(s) to use. If not specified, it defaults to the space character." | ||||
|     }, | ||||
|     "$fromMillis": { | ||||
|         "args": "number", | ||||
|         "desc": "Convert a number representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a timestamp string in the ISO 8601 format." | ||||
|         "args": "number, [, picture [, timezone]]", | ||||
|         "desc": "Convert the `number` representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a formatted string representation of the timestamp as specified by the picture string.\n\nIf the optional `picture` parameter is omitted, then the timestamp is formatted in the ISO 8601 format.\n\nIf the optional `picture` string is supplied, then the timestamp is formatted occording to the representation specified in that string. The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function `format-dateTime` as defined in the XPath F&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the same syntax as `format-dateTime`.\n\nIf the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone. The `timezone` string should be in the format '±HHMM', where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC." | ||||
|     }, | ||||
|     "$formatNumber": { | ||||
|         "args": "number, picture [, options]", | ||||
|   | ||||
| @@ -54,7 +54,16 @@ | ||||
|         "delete": "本当に '__label__' を削除しますか?", | ||||
|         "dropFlowHere": "ここにフローをドロップしてください", | ||||
|         "addFlow": "フローの追加", | ||||
|         "addFlowToRight": "右側にフローを追加", | ||||
|         "hideFlow": "フローを非表示", | ||||
|         "hideOtherFlows": "他のフローを非表示", | ||||
|         "showAllFlows": "全てのフローを表示", | ||||
|         "hideAllFlows": "全てのフローを非表示", | ||||
|         "hiddenFlows": "__count__ 個の非表示のフロー一覧", | ||||
|         "hiddenFlows_plural": "__count__ 個の非表示のフロー一覧", | ||||
|         "showLastHiddenFlow": "最後に非表示にしたフローを表示", | ||||
|         "listFlows": "フロー一覧", | ||||
|         "listSubflows": "サブフロー一覧", | ||||
|         "status": "状態", | ||||
|         "enabled": "有効", | ||||
|         "disabled": "無効", | ||||
| @@ -66,6 +75,8 @@ | ||||
|             "view": { | ||||
|                 "view": "表示", | ||||
|                 "grid": "グリッド", | ||||
|                 "storeZoom": "読み込み時に拡大/縮小のレベルを復元", | ||||
|                 "storePosition": "読み込み時にスクロール位置を復元", | ||||
|                 "showGrid": "グリッドを表示", | ||||
|                 "snapGrid": "ノードの配置を補助", | ||||
|                 "gridSize": "グリッドの大きさ", | ||||
| @@ -83,6 +94,7 @@ | ||||
|             "palette": { | ||||
|                 "show": "パレットを表示" | ||||
|             }, | ||||
|             "edit": "編集", | ||||
|             "settings": "設定", | ||||
|             "userSettings": "ユーザ設定", | ||||
|             "nodes": "ノード", | ||||
| @@ -105,24 +117,43 @@ | ||||
|             "editPalette": "パレットの管理", | ||||
|             "other": "その他", | ||||
|             "showTips": "ヒントを表示", | ||||
|             "showWelcomeTours": "新バージョンのガイドツアーを表示", | ||||
|             "help": "Node-REDウェブサイト", | ||||
|             "projects": "プロジェクト", | ||||
|             "projects-new": "新規", | ||||
|             "projects-open": "開く", | ||||
|             "projects-settings": "設定", | ||||
|             "showNodeLabelDefault": "追加したノードのラベルを表示", | ||||
|             "codeEditor": "コードエディタ", | ||||
|             "groups": "グループ", | ||||
|             "groupSelection": "選択部分をグループ化", | ||||
|             "ungroupSelection": "選択部分をグループ解除", | ||||
|             "groupMergeSelection": "選択部分をマージ", | ||||
|             "groupRemoveSelection": "グループから削除" | ||||
|             "groupRemoveSelection": "グループから削除", | ||||
|             "arrange": "配置", | ||||
|             "alignLeft": "左揃え", | ||||
|             "alignCenter": "左右中央揃え", | ||||
|             "alignRight": "右揃え", | ||||
|             "alignTop": "上揃え", | ||||
|             "alignMiddle": "上下中央揃え", | ||||
|             "alignBottom": "下揃え", | ||||
|             "distributeHorizontally": "左右に整列", | ||||
|             "distributeVertically": "上下に整列", | ||||
|             "moveToBack": "最背面へ移動", | ||||
|             "moveToFront": "最前面へ移動", | ||||
|             "moveBackwards": "背面へ移動", | ||||
|             "moveForwards": "前面へ移動" | ||||
|         } | ||||
|     }, | ||||
|     "actions": { | ||||
|         "toggle-navigator": "ナビゲータの表示/非表示を切替", | ||||
|         "zoom-out": "縮小", | ||||
|         "zoom-reset": "拡大/縮小を初期化", | ||||
|         "zoom-in": "拡大" | ||||
|         "zoom-in": "拡大", | ||||
|         "search-flows": "フローを検索", | ||||
|         "search-prev": "前へ", | ||||
|         "search-next": "次へ", | ||||
|         "search-counter": "\"__term__\" __count__ 件中の __result__ 件目" | ||||
|     }, | ||||
|     "user": { | ||||
|         "loggedInAs": "__name__ としてログインしました", | ||||
| @@ -186,7 +217,8 @@ | ||||
|             "create-default-package": "デフォルトパッケージファイルの作成", | ||||
|             "no-thanks": "不要", | ||||
|             "create-default-project": "デフォルトプロジェクトファイルの作成", | ||||
|             "show-merge-conflicts": "マージ競合を表示" | ||||
|             "show-merge-conflicts": "マージ競合を表示", | ||||
|             "unknownNodesButton": "不明なノードを検索する" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -270,7 +302,9 @@ | ||||
|         "successfulRestart": "フローの再起動が成功しました", | ||||
|         "deployFailed": "デプロイが失敗しました: __message__", | ||||
|         "unusedConfigNodes": "使われていない設定ノードがあります。", | ||||
|         "unusedConfigNodesLink": "設定を参照する", | ||||
|         "unusedConfigNodesButton": "未使用の構成ノードを検索", | ||||
|         "unknownNodesButton": "不明なノードを検索する", | ||||
|         "invalidNodesButton": "無効なノードを検索する", | ||||
|         "errors": { | ||||
|             "noResponse": "サーバの応答がありません" | ||||
|         }, | ||||
| @@ -450,7 +484,8 @@ | ||||
|         "global": "グローバル", | ||||
|         "workspace": "ワークスペース", | ||||
|         "selectAll": "全てのノードを選択", | ||||
|         "selectAllConnected": "接続された全てのノードを選択", | ||||
|         "selectNone": "選択を外す", | ||||
|         "selectAllConnected": "接続されたノードを選択", | ||||
|         "addRemoveNode": "ノードの選択、選択解除", | ||||
|         "editSelected": "選択したノードを編集", | ||||
|         "deleteSelected": "選択したノードや接続を削除", | ||||
| @@ -460,13 +495,17 @@ | ||||
|         "moveNode": "選択したノードを移動(移動量大)", | ||||
|         "toggleSidebar": "サイドバーの表示/非表示", | ||||
|         "togglePalette": "パレットの表示/非表示", | ||||
|         "copyNode": "選択したノードをコピー", | ||||
|         "cutNode": "選択したノードを切り取り", | ||||
|         "copyNode": "ノードをコピー", | ||||
|         "cutNode": "ノードを切り取り", | ||||
|         "pasteNode": "ノードを貼り付け", | ||||
|         "copyGroupStyle": "グループ様式をコピー", | ||||
|         "pasteGroupStyle": "グループ様式を貼り付け", | ||||
|         "undoChange": "変更操作を戻す", | ||||
|         "redoChange": "変更操作をやり直し", | ||||
|         "searchBox": "ノードを検索", | ||||
|         "managePalette": "パレットの管理", | ||||
|         "actionList": "動作一覧" | ||||
|         "actionList": "動作一覧", | ||||
|         "splitWireWithLinks": "選択したワイヤーをlinkノードで分離" | ||||
|     }, | ||||
|     "library": { | ||||
|         "library": "ライブラリ", | ||||
| @@ -518,7 +557,8 @@ | ||||
|             "nodeEnabled_plural": "ノードを有効化しました:", | ||||
|             "nodeDisabled": "ノードを無効化しました:", | ||||
|             "nodeDisabled_plural": "ノードを無効化しました:", | ||||
|             "nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました" | ||||
|             "nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました", | ||||
|             "unknownNodeRegistered": "ノードの読み込みエラー: <ul><li>__type__<br>__error__</li></ul>" | ||||
|         }, | ||||
|         "editor": { | ||||
|             "title": "パレットの管理", | ||||
| @@ -635,14 +675,7 @@ | ||||
|             "empty": "空", | ||||
|             "globalConfig": "グローバル設定ノード", | ||||
|             "triggerAction": "アクションを実行", | ||||
|             "find": "ワークスペース内を検索", | ||||
|             "search": { | ||||
|                 "configNodes": "設定ノード", | ||||
|                 "unusedConfigNodes": "未使用の設定ノード", | ||||
|                 "invalidNodes": "不正なノード", | ||||
|                 "uknownNodes": "未知のノード", | ||||
|                 "unusedSubflows": "未使用のサブフロー" | ||||
|             } | ||||
|             "find": "ワークスペース内を検索" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "ヘルプ", | ||||
| @@ -863,8 +896,19 @@ | ||||
|         "addTitle": "要素を追加" | ||||
|     }, | ||||
|     "search": { | ||||
|         "history": "検索履歴", | ||||
|         "clear": "全て削除", | ||||
|         "empty": "一致したものが見つかりませんでした", | ||||
|         "addNode": "ノードを追加..." | ||||
|         "addNode": "ノードを追加...", | ||||
|         "options": { | ||||
|             "configNodes": "設定ノード", | ||||
|             "unusedConfigNodes": "未使用の設定ノード", | ||||
|             "invalidNodes": "不正なノード", | ||||
|             "uknownNodes": "未知のノード", | ||||
|             "unusedSubflows": "未使用のサブフロー", | ||||
|             "hiddenFlows": "非表示のフロー", | ||||
|             "modifiedNodes": "修正したノードやフロー" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "関数", | ||||
| @@ -885,6 +929,9 @@ | ||||
|             "eval": "表現評価エラー:\n  __message__" | ||||
|         } | ||||
|     }, | ||||
|     "monaco": { | ||||
|         "setTheme": "テーマを設定:" | ||||
|     }, | ||||
|     "jsEditor": { | ||||
|         "title": "JavaScriptエディタ" | ||||
|     }, | ||||
| @@ -896,6 +943,8 @@ | ||||
|         "format": "JSONフォーマット", | ||||
|         "rawMode": "JSONを編集", | ||||
|         "uiMode": "ビジュアルエディタ", | ||||
|         "rawMode-readonly": "JSON", | ||||
|         "uiMode-readonly": "ビジュアル", | ||||
|         "insertAbove": "上に挿入", | ||||
|         "insertBelow": "下に挿入", | ||||
|         "addItem": "要素を追加", | ||||
| @@ -1057,7 +1106,8 @@ | ||||
|             "not-git": "Gitリポジトリではありません", | ||||
|             "no-resource": "リポジトリが見つかりません", | ||||
|             "cant-get-ssh-key-path": "エラー! 選択したSSHキーのパスを取得できません。", | ||||
|             "unexpected_error": "予期しないエラー" | ||||
|             "unexpected_error": "予期しないエラー", | ||||
|             "clearContext": "プロジェクトを切り替る際にコンテキストを初期化" | ||||
|         }, | ||||
|         "delete": { | ||||
|             "confirm": "プロジェクトを削除しても良いですか?" | ||||
| @@ -1104,6 +1154,14 @@ | ||||
|         "preview": "UIプレビュー", | ||||
|         "defaultValue": "デフォルト値" | ||||
|     }, | ||||
|     "tourGuide": { | ||||
|         "takeATour": "ツアーを開始", | ||||
|         "start": "開始", | ||||
|         "next": "次へ" | ||||
|     }, | ||||
|     "diagnostics": { | ||||
|         "title": "システム情報" | ||||
|     }, | ||||
|     "languages": { | ||||
|         "de": "ドイツ語", | ||||
|         "en-US": "英語", | ||||
| @@ -1112,5 +1170,159 @@ | ||||
|         "ru": "ロシア語", | ||||
|         "zh-CN": "中国語(簡体)", | ||||
|         "zh-TW": "中国語(繁体)" | ||||
|     }, | ||||
|     "validator": { | ||||
|         "errors": { | ||||
|             "invalid-json": "JSONデータが不正: __error__", | ||||
|             "invalid-json-prop": "__prop__: JSONデータが不正: __error__", | ||||
|             "invalid-prop": "プロパティ式が不正", | ||||
|             "invalid-prop-prop": "__prop__: プロパティ式が不正", | ||||
|             "invalid-num": "数値が不正", | ||||
|             "invalid-num-prop": "__prop__: 数値が不正", | ||||
|             "invalid-regexp": "入力パターンが不正", | ||||
|             "invalid-regex-prop": "__prop__: 入力パターンが不正", | ||||
|             "missing-required-prop": "__prop__: プロパティが未設定", | ||||
|             "invalid-config": "__prop__: 設定ノードが不正", | ||||
|             "missing-config": "__prop__: 設定ノードが存在しません", | ||||
|             "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" | ||||
|         } | ||||
|     }, | ||||
|     "action-list": { | ||||
|         "toggle-show-tips": "ヒント表示切替", | ||||
|         "show-about": "Node-REDの説明を表示", | ||||
|         "show-welcome-tour": "ウェルカムツアー表示", | ||||
|         "show-next-tab": "次のタブを表示", | ||||
|         "show-previous-tab": "前のタブを表示", | ||||
|         "add-flow": "フローを追加", | ||||
|         "add-flow-to-right": "フローを右に追加", | ||||
|         "edit-flow": "フローを編集", | ||||
|         "remove-flow": "フローを削除", | ||||
|         "enable-flow": "フローを有効化", | ||||
|         "disable-flow": "フローを無効化", | ||||
|         "hide-flow": "フローを隠す", | ||||
|         "hide-other-flows": "他のフローを非表示", | ||||
|         "hide-all-flows": "全てのフローを非表示", | ||||
|         "show-all-flows": "全てのフローを表示", | ||||
|         "show-last-hidden-flow": "最後に非表示にしたフローを表示", | ||||
|         "list-modified-nodes": "修正したフローを表示", | ||||
|         "list-hidden-flows": "非表示フローを表示", | ||||
|         "list-flows": "フロー一覧", | ||||
|         "list-subflows": "サブフロー一覧", | ||||
|         "go-to-previous-location": "前の位置に移動", | ||||
|         "go-to-next-location": "次の位置に移動", | ||||
|         "copy-selection-to-internal-clipboard": "選択をクリップボードにコピー", | ||||
|         "cut-selection-to-internal-clipboard": "選択をクリップボードに切り取り", | ||||
|         "paste-from-internal-clipboard": "クリップボードから貼り付け", | ||||
|         "detach-selected-nodes": "選択ノードを接続から外す", | ||||
|         "delete-selection": "選択を削除", | ||||
|         "delete-selection-and-reconnect": "選択を削除し再接続", | ||||
|         "edit-selected-node": "選択したノードを編集", | ||||
|         "go-to-selection": "選択に移動", | ||||
|         "undo": "変更操作を戻す", | ||||
|         "redo": "変更操作をやり直し", | ||||
|         "select-all-nodes": "全てのノードを選択", | ||||
|         "select-none": "ノードを選択", | ||||
|         "enable-selected-nodes": "選択ノードを有効化", | ||||
|         "disable-selected-nodes": "選択ノードを無効化", | ||||
|         "toggle-show-grid": "グリッド表示切替", | ||||
|         "toggle-snap-grid": "ノードの配置補助切替", | ||||
|         "toggle-status": "ステータス表示切替", | ||||
|         "show-selected-node-labels": "選択したノードのラベルを表示", | ||||
|         "hide-selected-node-labels": "選択したノードのラベルを非表示", | ||||
|         "scroll-view-up": "上スクロール", | ||||
|         "scroll-view-right": "右スクロール", | ||||
|         "scroll-view-down": "下スクロール", | ||||
|         "scroll-view-left": "左スクロール", | ||||
|         "step-view-up": "一単位上スクロール", | ||||
|         "step-view-right": "一単位右スクロール", | ||||
|         "step-view-down": "一単位下スクロール", | ||||
|         "step-view-left": "一単位左スクロール", | ||||
|         "move-selection-up": "選択を上移動", | ||||
|         "move-selection-right": "選択を右移動", | ||||
|         "move-selection-down": "選択を下移動", | ||||
|         "move-selection-left": "選択を左移動", | ||||
|         "move-selection-forwards": "選択を前面に移動", | ||||
|         "move-selection-backwards": "選択を背面に移動", | ||||
|         "move-selection-to-front": "選択を最前面に移動", | ||||
|         "move-selection-to-back": "選択を最背面に移動", | ||||
|         "step-selection-up": "選択を一単位上移動", | ||||
|         "step-selection-right": "選択を一単位右移動", | ||||
|         "step-selection-down": "選択を一単位下移動", | ||||
|         "step-selection-left": "選択を一単位左移動", | ||||
|         "select-connected-nodes": "接続されたノードを選択", | ||||
|         "select-downstream-nodes": "後方に接続されたノードを選択", | ||||
|         "select-upstream-nodes": "前方に接続されたノードを選択", | ||||
|         "go-to-next-node": "次のノードに移動", | ||||
|         "go-to-previous-node": "前のノードに移動", | ||||
|         "go-to-next-sibling": "次の兄弟ノードに移動", | ||||
|         "go-to-previous-sibling": "前の兄弟ノードに移動", | ||||
|         "go-to-nearest-node-on-left": "最も近い左側ノードに移動", | ||||
|         "go-to-nearest-node-on-right": "最も近い右側ノードに移動", | ||||
|         "go-to-nearest-node-above": "最も近い上側ノードに移動", | ||||
|         "go-to-nearest-node-below": "最も近い下側ノードに移動", | ||||
|         "align-selection-to-grid": "選択を整列", | ||||
|         "align-selection-to-left": "選択を左揃え", | ||||
|         "align-selection-to-right": "選択を右揃え", | ||||
|         "align-selection-to-top": "選択を上揃え", | ||||
|         "align-selection-to-bottom": "選択を下揃え", | ||||
|         "align-selection-to-middle": "選択を上下中央揃え", | ||||
|         "align-selection-to-center": "選択を左右中央揃え", | ||||
|         "distribute-selection-horizontally": "選択を左右に整列", | ||||
|         "distribute-selection-vertically": "選択を上下に整列", | ||||
|         "wire-series-of-nodes": "ノードを一続きに接続", | ||||
|         "wire-node-to-multiple": "ノードを複数に接続", | ||||
|         "split-wire-with-link-nodes": "ワイヤーをlinkノードで分割", | ||||
|         "generate-node-names": "ノード名を生成", | ||||
|         "show-user-settings": "ユーザ設定を表示", | ||||
|         "show-help": "ヘルプを表示", | ||||
|         "toggle-palette": "パレットの表示切替", | ||||
|         "show-event-log": "イベントログを表示", | ||||
|         "manage-palette": "パレットの管理", | ||||
|         "toggle-sidebar": "サイドバーの表示切替", | ||||
|         "show-info-tab": "ノード情報タブの表示", | ||||
|         "show-help-tab": "ノードヘルプタブの表示", | ||||
|         "show-config-tab": "設定ノードタブの表示", | ||||
|         "select-all-config-nodes": "全ての設定ノードを選択", | ||||
|         "delete-config-selection": "選択した設定ノードを削除", | ||||
|         "show-context-tab": "コンテキストデータタブを表示", | ||||
|         "create-subflow": "サブフローを作成", | ||||
|         "convert-to-subflow": "選択をサブフローに変換", | ||||
|         "group-selection": "選択をグループ化", | ||||
|         "ungroup-selection": "選択をグループ解除", | ||||
|         "merge-selection-to-group": "選択をグループにマージ", | ||||
|         "remove-selection-from-group": "選択をグループから削除", | ||||
|         "copy-group-style": "グループのスタイルをコピー", | ||||
|         "paste-group-style": "グループのスタイルを貼り付け", | ||||
|         "show-export-dialog": "書き出しダイアログを表示", | ||||
|         "show-import-dialog": "読み込みダイアログを表示", | ||||
|         "show-library-export-dialog": "ライブラリ書き出しダイアログを表示", | ||||
|         "show-library-import-dialog": "ライブラリ読み込みダイアログを表示", | ||||
|         "show-examples-import-dialog": "サンプル読み込みダイアログを表示", | ||||
|         "search": "検索", | ||||
|         "search-previous": "前を検索", | ||||
|         "search-next": "次を検索", | ||||
|         "show-action-list": "アクション一覧を表示", | ||||
|         "confirm-edit-tray": "編集を完了", | ||||
|         "cancel-edit-tray": "編集をキャンセル", | ||||
|         "show-remote-diff": "リモートとの変更差分を表示", | ||||
|         "deploy-flows": "フローをデプロイ", | ||||
|         "restart-flows": "フローを再起動", | ||||
|         "set-deploy-type-to-full": "デプロイを「全て」に設定", | ||||
|         "set-deploy-type-to-modified-flows": "デプロイを「変更したフロー」に設定", | ||||
|         "set-deploy-type-to-modified-nodes": "デプロイを「変更したノード」に設定", | ||||
|         "show-debug-tab": "デバッグタブを表示", | ||||
|         "clear-debug-messages": "デバッグメッセージを削除", | ||||
|         "clear-filtered-debug-messages": "フィルタしたデバッグメッセージを削除", | ||||
|         "activate-selected-debug-nodes": "選択したデバッグノードを有効化", | ||||
|         "activate-all-debug-nodes": "全てのデバッグノードを有効化", | ||||
|         "activate-all-flow-debug-nodes": "フロー内の全デバッグノードを有効化", | ||||
|         "deactivate-selected-debug-nodes": "選択したデバッグノードを無効化", | ||||
|         "deactivate-all-debug-nodes": "全てのデバッグノードを無効化", | ||||
|         "deactivate-all-flow-debug-nodes": "フロー内の全デバッグノードを無効化", | ||||
|         "zoom-in": "ズームイン", | ||||
|         "zoom-out": "ズームアウト", | ||||
|         "zoom-reset": "ズームリセット", | ||||
|         "toggle-navigator": "ナビゲータ表示切替", | ||||
|         "show-system-info": "システムインフォメーション" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -52,8 +52,8 @@ | ||||
|         "desc": "文字列 `str` からパターン `pattern` を検索し、置換文字列 `replacement` に置き換えます。\n\n任意の引数 `limit` には、置換回数の上限値を指定します。" | ||||
|     }, | ||||
|     "$now": { | ||||
|         "args": "", | ||||
|         "desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。" | ||||
|         "args": "$[picture [, timezone]]", | ||||
|         "desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。pictureおよびtimezoneパラメータが指定されている場合、現在時刻を`$fromMillis()`関数の説明に従ってフォーマットします。" | ||||
|     }, | ||||
|     "$base64encode": { | ||||
|         "args": "string", | ||||
| @@ -200,8 +200,8 @@ | ||||
|         "desc": "文字数が引数 `width` の絶対値以上となるよう、必要に応じて追加文字を付け足した `string` のコピーを返します。\n\n`width` が正の値の場合、文字列の右側に追加文字を付け足します。もし負の値の場合、文字列の左側に追加文字を付け足します。\n\n任意の引数 `char` には、本関数で用いる追加文字を指定します。もし追加文字を指定しない場合は、既定値として空白文字を使用します。" | ||||
|     }, | ||||
|     "$fromMillis": { | ||||
|         "args": "number", | ||||
|         "desc": "Unixエポック(1 January, 1970 UTC)からの経過ミリ秒を表す数値を、ISO 8601形式のタイムスタンプの文字列に変換します。" | ||||
|         "args": "number, [, picture [, timezone]]", | ||||
|         "desc": "Unixエポック(1 January, 1970 UTC)からの経過ミリ秒を表す数値を、`picture`の指定に従ってタイムスタンプの文字列に変換します。\n\n`picture`パラメータが指定されない場合、ISO 8601形式に変換します。\n\n`picture`を指定すると、指定した文字列に従って変換を行います。この変換はXPath F&O 3.1仕様におけるXPath/XQueryの2引数形式`format-dateTime`と同様です。`picture`パラメータはタイムスタンプの変換形式を定義し、その書式は`format-dateTime`と同じです。\n\n`timezone`を指定すると、指定タイムゾーンで変換します。`timezone`は'±HHMM'という形式で指定します。ここで、±は+もしくは-記号を表し、HHMMはUTCからの差分時間と分を表します。正の差分はUTCの東、負の差分は西のタイムゾーンとなります。" | ||||
|     }, | ||||
|     "$formatNumber": { | ||||
|         "args": "number, picture [, options]", | ||||
|   | ||||
| @@ -56,7 +56,7 @@ | ||||
|       "displayConfig": "설정노드 보기", | ||||
|       "import": "가져오기", | ||||
|       "export": "내보내기", | ||||
|       "search": "플로우 겅색", | ||||
|       "search": "플로우 검색", | ||||
|       "searchInput": "플로우 검색", | ||||
|       "subflows": "보조 플로우", | ||||
|       "createSubflow": "보조 플로우 생성", | ||||
| @@ -141,7 +141,8 @@ | ||||
|       "create-default-package": "기본 패키지 파일 생성", | ||||
|       "no-thanks": "괜찮습니다", | ||||
|       "create-default-project": "기본 프로젝트 파일 생성", | ||||
|       "show-merge-conflicts": "병합 충돌 보여주기" | ||||
|       "show-merge-conflicts": "병합 충돌 보여주기", | ||||
|       "unknownNodesButton": "알 수 없는 노드 검색" | ||||
|     } | ||||
|   }, | ||||
|   "clipboard": { | ||||
| @@ -203,7 +204,9 @@ | ||||
|     "successfulRestart": "플로우 재시작을 성공했습니다", | ||||
|     "deployFailed": "배포 실패 : __message__", | ||||
|     "unusedConfigNodes": "사용되지 않는 설정노드가 있습니다", | ||||
|     "unusedConfigNodesLink": "여기를 클릭하면 볼 수 있습니다", | ||||
|     "unusedConfigNodesButton":"사용하지 않는 구성 노드 검색", | ||||
|     "unknownNodesButton":"알 수 없는 노드 검색", | ||||
|     "invalidNodesButton":"잘못된 노드 검색", | ||||
|     "errors": { | ||||
|       "noResponse": "서버의 응답이 없습니다" | ||||
|     }, | ||||
|   | ||||
| @@ -183,7 +183,8 @@ | ||||
|             "create-default-package": "Создать файл пакета по умолчанию", | ||||
|             "no-thanks": "Нет, спасибо", | ||||
|             "create-default-project": "Создать файлы проекта по умолчанию", | ||||
|             "show-merge-conflicts": "Показать конфликты слияния" | ||||
|             "show-merge-conflicts": "Показать конфликты слияния", | ||||
|             "unknownNodesButton": "Поиск неизвестных узлов" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -277,7 +278,9 @@ | ||||
|         "successfulRestart": "Потоки успешно перезапущены", | ||||
|         "deployFailed": "Развертывание не удалось: __message__", | ||||
|         "unusedConfigNodes":"У вас есть неиспользуемых узлы конфигурации.", | ||||
|         "unusedConfigNodesLink":"Нажмите здесь, чтобы их увидеть", | ||||
|         "unusedConfigNodesButton":"Поиск неиспользуемых узлов конфигурации", | ||||
|         "unknownNodesButton":"Поиск неизвестных узлов", | ||||
|         "invalidNodesButton":"Поиск недопустимых узлов", | ||||
|         "errors": { | ||||
|             "noResponse": "нет ответа от сервера" | ||||
|         }, | ||||
| @@ -650,14 +653,7 @@ | ||||
|             "empty": "пусто", | ||||
|             "globalConfig": "Глобальные конфиг узлы", | ||||
|             "triggerAction": "Вызвать действие", | ||||
|             "find": "Найти в рабочей области", | ||||
|             "search": { | ||||
|                 "configNodes": "Узлы конфигурации", | ||||
|                 "unusedConfigNodes": "Неиспользуемые узлы конфигурации", | ||||
|                 "invalidNodes": "Недействительные узлы", | ||||
|                 "uknownNodes": "Неизвестные узлы", | ||||
|                 "unusedSubflows": "Неиспользуемые подпотоки" | ||||
|             } | ||||
|             "find": "Найти в рабочей области" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "Справка", | ||||
| @@ -888,7 +884,14 @@ | ||||
|     }, | ||||
|     "search": { | ||||
|         "empty": "Ничего не найдено", | ||||
|         "addNode": "добавить узел..." | ||||
|         "addNode": "добавить узел...", | ||||
|         "options": { | ||||
|             "configNodes": "Узлы конфигурации", | ||||
|             "unusedConfigNodes": "Неиспользуемые узлы конфигурации", | ||||
|             "invalidNodes": "Недействительные узлы", | ||||
|             "uknownNodes": "Неизвестные узлы", | ||||
|             "unusedSubflows": "Неиспользуемые подпотоки" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "Функции", | ||||
|   | ||||
| @@ -182,7 +182,8 @@ | ||||
|             "create-default-package": "创建默认的包文件", | ||||
|             "no-thanks": "不了,谢谢", | ||||
|             "create-default-project": "创建默认项目文件", | ||||
|             "show-merge-conflicts": "显示合并冲突" | ||||
|             "show-merge-conflicts": "显示合并冲突", | ||||
|             "unknownNodesButton": "搜索未知节点" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -225,7 +226,7 @@ | ||||
|             "compact": "紧凑", | ||||
|             "formatted": "已格式化", | ||||
|             "copy": "导出到剪贴板", | ||||
|             "export": "到处到库", | ||||
|             "export": "导出到库", | ||||
|             "exportAs": "导出为", | ||||
|             "overwrite": "替换", | ||||
|             "exists": "<p><b>\"__file__\"</b>已存在</p><p>是否要替换它?</p>" | ||||
| @@ -264,7 +265,9 @@ | ||||
|         "successfulRestart": "成功重启流程", | ||||
|         "deployFailed": "部署失败: __message__", | ||||
|         "unusedConfigNodes": "您有一些未使用的配置节点", | ||||
|         "unusedConfigNodesLink": "点击此处查看它们", | ||||
|         "unusedConfigNodesButton":"搜索未使用的配置节点", | ||||
|         "unknownNodesButton":"搜索未知节点", | ||||
|         "invalidNodesButton":"搜索无效节点", | ||||
|         "errors": { | ||||
|             "noResponse": "服务器没有响应" | ||||
|         }, | ||||
| @@ -614,14 +617,7 @@ | ||||
|             "empty": "空的", | ||||
|             "globalConfig": "全局配置节点", | ||||
|             "triggerAction": "触发动作", | ||||
|             "find": "在工作区中查找", | ||||
|             "search": { | ||||
|                 "configNodes": "配置节点", | ||||
|                 "unusedConfigNodes": "未使用的配置节点", | ||||
|                 "invalidNodes": "无效的节点", | ||||
|                 "uknownNodes": "未知的节点", | ||||
|                 "unusedSubflows": "未使用的子流程" | ||||
|             } | ||||
|             "find": "在工作区中查找" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "帮助", | ||||
| @@ -648,7 +644,7 @@ | ||||
|         }, | ||||
|         "context": { | ||||
|             "name": "上下文数据", | ||||
|             "label": "上下午", | ||||
|             "label": "上下文", | ||||
|             "none": "未选择", | ||||
|             "refresh": "刷新以加载", | ||||
|             "empty": "空", | ||||
| @@ -842,7 +838,14 @@ | ||||
|     }, | ||||
|     "search": { | ||||
|         "empty": "找不到匹配", | ||||
|         "addNode": "添加一个节点..." | ||||
|         "addNode": "添加一个节点...", | ||||
|         "options": { | ||||
|             "configNodes": "配置节点", | ||||
|             "unusedConfigNodes": "未使用的配置节点", | ||||
|             "invalidNodes": "无效的节点", | ||||
|             "uknownNodes": "未知的节点", | ||||
|             "unusedSubflows": "未使用的子流程" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "功能", | ||||
|   | ||||
| @@ -182,7 +182,8 @@ | ||||
|             "create-default-package": "創建默認的包文件", | ||||
|             "no-thanks": "不了,謝謝", | ||||
|             "create-default-project": "創建默認項目文件", | ||||
|             "show-merge-conflicts": "顯示合併衝突" | ||||
|             "show-merge-conflicts": "顯示合併衝突", | ||||
|             "unknownNodesButton": "搜索未知節點" | ||||
|         } | ||||
|     }, | ||||
|     "clipboard": { | ||||
| @@ -264,7 +265,9 @@ | ||||
|         "successfulRestart": "成功重啟流程", | ||||
|         "deployFailed": "部署失敗: __message__", | ||||
|         "unusedConfigNodes": "您有一些未使用的配置節點", | ||||
|         "unusedConfigNodesLink": "點擊此處查看它們", | ||||
|         "unusedConfigNodesButton":"搜索未使用的配置節點", | ||||
|         "unknownNodesButton":"搜索未知節點", | ||||
|         "invalidNodesButton":"搜索無效節點", | ||||
|         "errors": { | ||||
|             "noResponse": "伺服器沒有回應" | ||||
|         }, | ||||
| @@ -614,14 +617,7 @@ | ||||
|             "empty": "空的", | ||||
|             "globalConfig": "全局配置節點", | ||||
|             "triggerAction": "觸發動作", | ||||
|             "find": "在工作區中查找", | ||||
|             "search": { | ||||
|                 "configNodes": "配置節點", | ||||
|                 "unusedConfigNodes": "未使用的配置節點", | ||||
|                 "invalidNodes": "無效的節點", | ||||
|                 "uknownNodes": "未知的節點", | ||||
|                 "unusedSubflows": "未使用的子流程" | ||||
|             } | ||||
|             "find": "在工作區中查找" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "幫助", | ||||
| @@ -842,7 +838,14 @@ | ||||
|     }, | ||||
|     "search": { | ||||
|         "empty": "找不到匹配", | ||||
|         "addNode": "添加一個節點..." | ||||
|         "addNode": "添加一個節點...", | ||||
|         "options": { | ||||
|             "configNodes": "配置節點", | ||||
|             "unusedConfigNodes": "未使用的配置節點", | ||||
|             "invalidNodes": "無效的節點", | ||||
|             "uknownNodes": "未知的節點", | ||||
|             "unusedSubflows": "未使用的子流程" | ||||
|         } | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "功能", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-client", | ||||
|     "version": "1.3.4", | ||||
|     "version": "3.0.0-beta.2", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
| @@ -14,5 +14,5 @@ | ||||
|             "name": "Dave Conway-Jones" | ||||
|         } | ||||
|     ], | ||||
|     "main": "./lib/index.js" | ||||
|     "main": "./index.js" | ||||
| } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ oop.inherits(NRJavaScriptWorker, Mirror); | ||||
|  | ||||
| (function() { | ||||
|     this.setOptions = function(options) { | ||||
|         this.options = { | ||||
|         o.options = { | ||||
|             // undef: true, | ||||
|             // unused: true, | ||||
|             esversion: 9, | ||||
| @@ -98,7 +98,7 @@ oop.inherits(NRJavaScriptWorker, Mirror); | ||||
|         if (options) { | ||||
|             for (var opt in options) { | ||||
|                 if (options.hasOwnProperty(opt)) { | ||||
|                     this.options[opt] = options.opt; | ||||
|                     o.options[opt] = options[opt]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
							
								
								
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/grip-horizontal.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/grip-horizontal.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg width="50" height="5" viewBox="0, 0, 50, 5" xmlns="http://www.w3.org/2000/svg"><path d="M0 1H50V4H0Z" fill="#CCC"/></svg> | ||||
| After Width: | Height: | Size: 127 B | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 192 B | 
							
								
								
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/grip.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/grip.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg width="5" height="50" viewBox="0, 0, 5, 50" xmlns="http://www.w3.org/2000/svg"><path d="M1 0H4V50H1Z" fill="#CCC"/></svg> | ||||
| After Width: | Height: | Size: 127 B | 
							
								
								
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/node-red-256.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/node_modules/@node-red/editor-client/src/images/node-red-256.svg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="512" width="512"><g transform="translate(0 -540.36)"><path fill="#8f0000" color="#000" d="M0 540.36h512v392H0z"/><rect ry="0" height="108.23" width="500.23" stroke="#fff" y="938.25" x="5.89" stroke-width="11.77" fill="#fff"/><path style="text-decoration-color:#000;isolation:auto;mix-blend-mode:normal;solid-color:#000;block-progression:tb;text-decoration-line:none;white-space:normal;text-indent:0;text-transform:none;text-decoration-style:solid" d="M122.88 305.82a4.46 4.46 0 0 0-4.38 4.42v.78c-2.23.1-4.04.54-5.33 1.43a10.5 10.5 0 0 0-3.18 3.87c-.71 1.3-1.3 2.41-2.15 3.2-.72.66-1.8 1.12-3.45 1.32a4.37 4.37 0 0 0-4.3-3.95H82.91a4.43 4.43 0 0 0-4.42 4.35v4.24a4.43 4.43 0 0 0 4.42 4.36h17.16a4.4 4.4 0 0 0 4.4-4.36v-1.6c9.72.14 12.46 2.6 15.59 5.33 3 2.62 6.66 5.38 15.43 5.5v.73a4.49 4.49 0 0 0 4.46 4.38h17.09c2.38 0 4.45-2 4.45-4.38v-4.24a4.49 4.49 0 0 0-4.45-4.38h-17.1c-2.38 0-4.45 2-4.45 4.38v.58c-8.1-.06-10.48-2.15-13.5-4.79-2.5-2.19-5.64-4.58-11.94-5.58 1.17-1.18 1.88-2.52 2.51-3.66.68-1.23 1.29-2.2 2.27-2.88.76-.52 1.98-.84 3.66-.94v.55c0 2.39 2 4.34 4.38 4.34h17.24a4.39 4.39 0 0 0 4.38-4.34v-4.24c0-2.38-2-4.42-4.38-4.42zm0 3h17.24c.8 0 1.38.62 1.38 1.42v4.24c0 .81-.57 1.34-1.38 1.34h-17.24c-.8 0-1.38-.53-1.38-1.34v-4.24c0-.8.57-1.42 1.38-1.42zm-39.96 11.02h17.16c.81 0 1.42.6 1.42 1.4v4.24c0 .81-.61 1.41-1.42 1.41H82.92c-.8 0-1.42-.6-1.42-1.4v-4.25c0-.8.61-1.4 1.42-1.4zm57.04 9.98h17.09c.8 0 1.45.57 1.45 1.38v4.17c0 .8-.65 1.45-1.45 1.45h-17.1c-.8 0-1.45-.65-1.45-1.45v-4.17c0-.8.65-1.38 1.46-1.38z" fill="#fff" color="#000" transform="matrix(4 0 0 4 -162 -450.91)"/><g fill="#8f0000"><path d="M91 954.34v8.45l-8 1.45v60.07H69.03l-28.53-47.1-.5.05v37.2l8 1.44v8.41H19v-8.4l7.45-1.45v-50.22L19 962.79v-8.46h21.48l28.37 47.1.15-.04v-37.15l-7-1.45v-8.45h29zM95 997.83q0-11.63 6.49-19.03 6.53-7.45 18.02-7.45 11.53 0 18.02 7.4 6.54 7.4 6.54 19.08v1q0 11.74-6.54 19.14-6.49 7.35-17.93 7.35-11.58 0-18.11-7.35-6.5-7.4-6.5-19.13v-1.01zm14.03 1q0 7.12 2.5 11.45 2.5 4.28 8.08 4.28 5.43 0 7.93-4.33 2.54-4.33 2.54-11.4v-1q0-6.92-2.54-11.3-2.55-4.37-8.03-4.37t-7.98 4.37-2.5 11.3v1zM184.48 1017.96a17.15 17.15 0 0 1-5.82 5.48 15.17 15.17 0 0 1-7.59 1.88c-6.4 0-11.4-2.35-14.95-7.03-3.52-4.67-5.13-10.86-5.13-18.55v-1c0-8.21 1.62-14.83 5.18-19.86 3.56-5.03 8.56-7.54 15-7.54 2.6 0 4.93.57 7.01 1.73a17.94 17.94 0 0 1 5.81 4.8v-18.64l-8-1.45v-8.46h22v65.13l6 1.44v8.43h-18.45l-1.06-6.36zm-19.49-18.22c0 4.55.63 8.14 2.14 10.77 1.54 2.6 4.04 3.9 7.5 3.9 2.05 0 3.83-.43 5.33-1.26 1.5-.83 3.07-2.03 4.03-3.6v-22.06a11.27 11.27 0 0 0-4.03-3.85 9.62 9.62 0 0 0-5.24-1.4c-3.43 0-5.92 1.53-7.5 4.57s-2.23 7.02-2.23 11.92v1.01zM233.7 1025.28c-7.5 0-13.5-2.4-17.98-7.21-4.48-4.81-6.73-10.91-6.73-18.32v-1.92c0-7.72 2.12-14.08 6.35-19.08 4.26-5 9.96-7.46 17.1-7.43 7.03 0 12.47 2.1 16.35 6.33a23.46 23.46 0 0 1 6.2 17.15v7.52h-31.43l-.1.41c.26 3.43 1.4 6.25 3.41 8.46 2.05 2.21 4.83 3.32 8.32 3.32 3.1 0 5.69-.3 7.74-.91 2.05-.64 4.29-1.64 6.73-2.98l3.8 8.65a27.59 27.59 0 0 1-8.37 4.28 35.28 35.28 0 0 1-11.4 1.73zm-1.25-43.16c-2.6 0-4.65.99-6.15 2.98s-2.44 4.6-2.8 7.83l.15.4H241v-1.41c0-2.98-.84-5.35-2.25-7.11-1.37-1.8-3.47-2.7-6.3-2.7zM291.99 1000.32h-27v-11h27zM331.88 954.34c7.95 0 14.18 1.82 18.7 5.47 4.52 3.63 6.4 8.64 6.4 15.05 0 3.52-.57 6.58-2.46 9.18-1.89 2.6-4.66 4.7-8.31 6.3 4.13 1.21 7.1 3.25 8.89 6.1a19.02 19.02 0 0 1 2.89 10.52v3.56c0 1.54.15 2.74.76 3.6.6.84 1.62 1.34 3.03 1.5l1.2.24v8.46h-6.73c-4.58 0-7.8-1.24-9.66-3.7s-2.6-5.66-2.6-9.57v-3.99c0-3.4-1.1-6.05-2.93-7.98-1.8-1.95-4.34-2.98-7.64-3.07H322v18.45l7 1.44v8.42h-29v-8.42l8-1.44v-50.22l-8-1.44v-8.46h31.89zm-9.95 30.85h9.71c3.91 0 6.84-.83 8.8-2.5s2.93-4.07 2.93-7.2c0-3.15-.98-5.65-2.93-7.5-1.92-1.9-4.78-2.84-8.56-2.84h-9.9v20.04zM412.99 993.32h-23v20h22.21l.63-8h10.2v18.99H368v-8.42l8-1.44v-50.22l-8-1.44v-8.47h54.95v19h-10.3l-.63-8H390v17h23v11zM462.48 954.36c8.55 0 15.6 2.71 21.14 8.19 5.55 5.45 8.36 12.42 8.36 20.98v11.58c0 8.59-2.81 15.63-8.36 21.08-5.54 5.41-12.59 8.12-21.14 8.12h-31.5v-8.41l7-1.52v-50.22l-7-1.37v-8.43l7.46-.08 24.04.08zm-10.5 10.76v48.4l9.77.02c5.03.02 8.98-1.7 11.83-5.1 2.85-3.39 4.4-7.8 4.4-13.28v-11.68c0-5.42-1.55-9.84-4.4-13.24-2.85-3.4-6.8-5.1-11.83-5.1l-9.77-.02z"/></g></g></svg> | ||||
| After Width: | Height: | Size: 4.2 KiB | 
| @@ -13,6 +13,11 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| /**  | ||||
|  * An API for undo / redo history buffer | ||||
|  * @namespace RED.history | ||||
| */ | ||||
| RED.history = (function() { | ||||
|     var undoHistory = []; | ||||
|     var redoHistory = []; | ||||
| @@ -66,12 +71,14 @@ RED.history = (function() { | ||||
|                     var importedResult = RED.nodes.import(ev.config,{importMap: importMap}) | ||||
|                     inverseEv = { | ||||
|                         t: 'replace', | ||||
|                         config: importedResult.removedNodes | ||||
|                         config: importedResult.removedNodes, | ||||
|                         dirty: RED.nodes.dirty() | ||||
|                     } | ||||
|                 } | ||||
|             } else if (ev.t == 'add') { | ||||
|                 inverseEv = { | ||||
|                     t: "delete", | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }; | ||||
|                 if (ev.nodes) { | ||||
|                     inverseEv.nodes = []; | ||||
| @@ -99,6 +106,23 @@ RED.history = (function() { | ||||
|                         RED.nodes.removeLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.junctions) { | ||||
|                     inverseEv.junctions = []; | ||||
|                     for (i=0;i<ev.junctions.length;i++) { | ||||
|                         inverseEv.junctions.push(ev.junctions[i]); | ||||
|                         RED.nodes.removeJunction(ev.junctions[i]); | ||||
|                         if (ev.junctions[i].g) { | ||||
|                             var group = RED.nodes.group(ev.junctions[i].g); | ||||
|                             var index = group.nodes.indexOf(ev.junctions[i]); | ||||
|                             if (index !== -1) { | ||||
|                                 group.nodes.splice(index,1); | ||||
|                                 RED.group.markDirty(group); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.groups) { | ||||
|                     inverseEv.groups = []; | ||||
|                     for (i = ev.groups.length - 1;i>=0;i--) { | ||||
| @@ -158,7 +182,8 @@ RED.history = (function() { | ||||
|  | ||||
|             } else if (ev.t == "delete") { | ||||
|                 inverseEv = { | ||||
|                     t: "add" | ||||
|                     t: "add", | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }; | ||||
|                 if (ev.workspaces) { | ||||
|                     inverseEv.workspaces = []; | ||||
| @@ -264,6 +289,21 @@ RED.history = (function() { | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.junctions) { | ||||
|                     inverseEv.junctions = []; | ||||
|                     for (i=0;i<ev.junctions.length;i++) { | ||||
|                         inverseEv.junctions.push(ev.junctions[i]); | ||||
|                         RED.nodes.addJunction(ev.junctions[i]); | ||||
|                         if (ev.junctions[i].g) { | ||||
|                             group = RED.nodes.group(ev.junctions[i].g); | ||||
|                             if (group.nodes.indexOf(ev.junctions[i]) === -1) { | ||||
|                                 group.nodes.push(ev.junctions[i]); | ||||
|                             } | ||||
|                             RED.group.markDirty(group) | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
|                     inverseEv.links = []; | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
| @@ -300,11 +340,12 @@ RED.history = (function() { | ||||
|             } else if (ev.t == "move") { | ||||
|                 inverseEv = { | ||||
|                     t: 'move', | ||||
|                     nodes: [] | ||||
|                     nodes: [], | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }; | ||||
|                 for (i=0;i<ev.nodes.length;i++) { | ||||
|                     var n = ev.nodes[i]; | ||||
|                     var rn = {n: n.n, ox: n.n.x, oy: n.n.y, dirty: true, moved: n.moved}; | ||||
|                     var rn = {n: n.n, ox: n.n.x, oy: n.n.y, dirty: true, moved: n.n.moved}; | ||||
|                     inverseEv.nodes.push(rn); | ||||
|                     n.n.x = n.ox; | ||||
|                     n.n.y = n.oy; | ||||
| @@ -336,7 +377,9 @@ RED.history = (function() { | ||||
|             } else if (ev.t == "edit") { | ||||
|                 inverseEv = { | ||||
|                     t: "edit", | ||||
|                     changes: {} | ||||
|                     changes: {}, | ||||
|                     changed: ev.node.changed, | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }; | ||||
|                 inverseEv.node = ev.node; | ||||
|                 for (i in ev.changes) { | ||||
| @@ -552,10 +595,22 @@ RED.history = (function() { | ||||
|             } else if (ev.t == "reorder") { | ||||
|                 inverseEv = { | ||||
|                     t: 'reorder', | ||||
|                     order: RED.nodes.getWorkspaceOrder() | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 }; | ||||
|                 if (ev.order) { | ||||
|                     RED.workspaces.order(ev.order); | ||||
|                 if (ev.workspaces) { | ||||
|                     inverseEv.workspaces = { | ||||
|                         from: ev.workspaces.to, | ||||
|                         to: ev.workspaces.from | ||||
|                     } | ||||
|                     RED.workspaces.order(ev.workspaces.from); | ||||
|                 } | ||||
|                 if (ev.nodes) { | ||||
|                     inverseEv.nodes = { | ||||
|                         z: ev.nodes.z, | ||||
|                         from: ev.nodes.to, | ||||
|                         to: ev.nodes.from | ||||
|                     } | ||||
|                     RED.nodes.setNodeOrder(ev.nodes.z,ev.nodes.from); | ||||
|                 } | ||||
|             } else if (ev.t == "createGroup") { | ||||
|                 inverseEv = { | ||||
| @@ -651,6 +706,8 @@ RED.history = (function() { | ||||
|         push: function(ev) { | ||||
|             undoHistory.push(ev); | ||||
|             redoHistory = []; | ||||
|             RED.menu.setDisabled("menu-item-edit-undo", false); | ||||
|             RED.menu.setDisabled("menu-item-edit-redo", true); | ||||
|         }, | ||||
|         pop: function() { | ||||
|             var ev = undoHistory.pop(); | ||||
| @@ -658,13 +715,24 @@ RED.history = (function() { | ||||
|             if (rev) { | ||||
|                 redoHistory.push(rev); | ||||
|             } | ||||
|             RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0); | ||||
|             RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0); | ||||
|         }, | ||||
|         peek: function() { | ||||
|             return undoHistory[undoHistory.length-1]; | ||||
|         }, | ||||
|         replace: function(ev) { | ||||
|             if (undoHistory.length === 0) { | ||||
|                 RED.history.push(ev); | ||||
|             } else { | ||||
|                 undoHistory[undoHistory.length-1] = ev; | ||||
|             } | ||||
|         }, | ||||
|         clear: function() { | ||||
|             undoHistory = []; | ||||
|             redoHistory = []; | ||||
|             RED.menu.setDisabled("menu-item-edit-undo", true); | ||||
|             RED.menu.setDisabled("menu-item-edit-redo", true); | ||||
|         }, | ||||
|         redo: function() { | ||||
|             var ev = redoHistory.pop(); | ||||
| @@ -674,6 +742,8 @@ RED.history = (function() { | ||||
|                     undoHistory.push(uev); | ||||
|                 } | ||||
|             } | ||||
|             RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0); | ||||
|             RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										156
									
								
								packages/node_modules/@node-red/editor-client/src/js/hooks.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								packages/node_modules/@node-red/editor-client/src/js/hooks.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| RED.hooks = (function() { | ||||
|  | ||||
|     var VALID_HOOKS = [ | ||||
|  | ||||
|     ] | ||||
|  | ||||
|     var hooks = { } | ||||
|     var labelledHooks = { } | ||||
|  | ||||
|     function add(hookId, callback) { | ||||
|         var parts = hookId.split("."); | ||||
|         var id = parts[0], label = parts[1]; | ||||
|  | ||||
|         // if (VALID_HOOKS.indexOf(id) === -1) { | ||||
|         //     throw new Error("Invalid hook '"+id+"'"); | ||||
|         // } | ||||
|         if (label && labelledHooks[label] && labelledHooks[label][id]) { | ||||
|             throw new Error("Hook "+hookId+" already registered") | ||||
|         } | ||||
|         var hookItem = {cb:callback, previousHook: null, nextHook: null } | ||||
|  | ||||
|         var tailItem = hooks[id]; | ||||
|         if (tailItem === undefined) { | ||||
|             hooks[id] = hookItem; | ||||
|         } else { | ||||
|             while(tailItem.nextHook !== null) { | ||||
|                 tailItem = tailItem.nextHook | ||||
|             } | ||||
|             tailItem.nextHook = hookItem; | ||||
|             hookItem.previousHook = tailItem; | ||||
|         } | ||||
|  | ||||
|         if (label) { | ||||
|             labelledHooks[label] = labelledHooks[label]||{}; | ||||
|             labelledHooks[label][id] = hookItem; | ||||
|         } | ||||
|     } | ||||
|     function remove(hookId) { | ||||
|         var parts = hookId.split("."); | ||||
|         var id = parts[0], label = parts[1]; | ||||
|         if ( !label) { | ||||
|             throw new Error("Cannot remove hook without label: "+hookId) | ||||
|         } | ||||
|         if (labelledHooks[label]) { | ||||
|             if (id === "*") { | ||||
|                 // Remove all hooks for this label | ||||
|                 var hookList = Object.keys(labelledHooks[label]); | ||||
|                 for (var i=0;i<hookList.length;i++) { | ||||
|                     removeHook(hookList[i],labelledHooks[label][hookList[i]]) | ||||
|                 } | ||||
|                 delete labelledHooks[label]; | ||||
|             } else if (labelledHooks[label][id]) { | ||||
|                 removeHook(id,labelledHooks[label][id]) | ||||
|                 delete labelledHooks[label][id]; | ||||
|                 if (Object.keys(labelledHooks[label]).length === 0){ | ||||
|                     delete labelledHooks[label]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function removeHook(id,hookItem) { | ||||
|         var previousHook = hookItem.previousHook; | ||||
|         var nextHook = hookItem.nextHook; | ||||
|  | ||||
|         if (previousHook) { | ||||
|             previousHook.nextHook = nextHook; | ||||
|         } else { | ||||
|             hooks[id] = nextHook; | ||||
|         } | ||||
|         if (nextHook) { | ||||
|             nextHook.previousHook = previousHook; | ||||
|         } | ||||
|         hookItem.removed = true; | ||||
|         if (!previousHook && !nextHook) { | ||||
|             delete hooks[id]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function trigger(hookId, payload, done) { | ||||
|         var hookItem = hooks[hookId]; | ||||
|         if (!hookItem) { | ||||
|             if (done) { | ||||
|                 done(); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         function callNextHook(err) { | ||||
|             if (!hookItem || err) { | ||||
|                 if (done) { done(err) } | ||||
|                 return err; | ||||
|             } | ||||
|             if (hookItem.removed) { | ||||
|                 hookItem = hookItem.nextHook; | ||||
|                 return callNextHook(); | ||||
|             } | ||||
|             var callback = hookItem.cb; | ||||
|             if (callback.length === 1) { | ||||
|                 try { | ||||
|                     let result = callback(payload); | ||||
|                     if (result === false) { | ||||
|                         // Halting the flow | ||||
|                         if (done) { done(false) } | ||||
|                         return result; | ||||
|                     } | ||||
|                     hookItem = hookItem.nextHook; | ||||
|                     return callNextHook(); | ||||
|                 } catch(e) { | ||||
|                     console.warn(e); | ||||
|                     if (done) { done(e);} | ||||
|                     return e; | ||||
|                 } | ||||
|             } else { | ||||
|                 // There is a done callback | ||||
|                 try { | ||||
|                     callback(payload,function(result) { | ||||
|                         if (result === undefined) { | ||||
|                             hookItem = hookItem.nextHook; | ||||
|                             callNextHook(); | ||||
|                         } else { | ||||
|                             if (done) { done(result)} | ||||
|                         } | ||||
|                     }) | ||||
|                 } catch(e) { | ||||
|                     console.warn(e); | ||||
|                     if (done) { done(e) } | ||||
|                     return e; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return callNextHook(); | ||||
|     } | ||||
|  | ||||
|     function clear() { | ||||
|         hooks = {} | ||||
|         labelledHooks = {} | ||||
|     } | ||||
|  | ||||
|     function has(hookId) { | ||||
|         var parts = hookId.split("."); | ||||
|         var id = parts[0], label = parts[1]; | ||||
|         if (label) { | ||||
|             return !!(labelledHooks[label] && labelledHooks[label][id]) | ||||
|         } | ||||
|         return !!hooks[id] | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         has: has, | ||||
|         clear: clear, | ||||
|         add: add, | ||||
|         remove: remove, | ||||
|         trigger: trigger | ||||
|     } | ||||
| })(); | ||||
| @@ -18,53 +18,69 @@ RED.i18n = (function() { | ||||
|  | ||||
|     var apiRootUrl; | ||||
|  | ||||
|     function detectLanguage() { | ||||
|         return navigator.language | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function(options, done) { | ||||
|             apiRootUrl = options.apiRootUrl||""; | ||||
|             var preferredLanguage = localStorage.getItem("editor-language"); | ||||
|             var preferredLanguage = localStorage.getItem("editor-language") || detectLanguage(); | ||||
|             var opts = { | ||||
|                 resGetPath: apiRootUrl+'locales/__ns__?lng=__lng__', | ||||
|                 dynamicLoad: false, | ||||
|                 load:'current', | ||||
|                 ns: { | ||||
|                     namespaces: ["editor","node-red","jsonata","infotips"], | ||||
|                     defaultNs: "editor" | ||||
|                 compatibilityJSON: 'v3', | ||||
|                 backend: { | ||||
|                     loadPath: apiRootUrl+'locales/__ns__?lng=__lng__', | ||||
|                 }, | ||||
|                 lng: 'en-US', | ||||
|                 // debug: true, | ||||
|                 preload:['en-US'], | ||||
|                 ns: ["editor","node-red","jsonata","infotips"], | ||||
|                 defaultNS: "editor", | ||||
|                 fallbackLng: ['en-US'], | ||||
|                 useCookie: false, | ||||
|                 returnObjectTrees: true | ||||
|                 returnObjects: true, | ||||
|                 keySeparator: ".", | ||||
|                 nsSeparator: ":", | ||||
|                 interpolation: { | ||||
|                     unescapeSuffix: 'HTML', | ||||
|                     escapeValue: false, | ||||
|                     prefix: '__', | ||||
|                     suffix: '__' | ||||
|                 } | ||||
|             }; | ||||
|             if (preferredLanguage) { | ||||
|                 opts.lng = preferredLanguage; | ||||
|             } | ||||
|             i18n.init(opts,function() { | ||||
|  | ||||
|             i18next.use(i18nextHttpBackend).init(opts,function() { | ||||
|                 done(); | ||||
|             }); | ||||
|             jqueryI18next.init(i18next, $, { handleName: 'i18n' }); | ||||
|  | ||||
|  | ||||
|             RED["_"] = function() { | ||||
|                 var v = i18n.t.apply(null,arguments); | ||||
|                 var v = i18next.t.apply(i18next,arguments); | ||||
|                 if (typeof v === 'string') { | ||||
|                     return v; | ||||
|                 } else { | ||||
|                     return arguments[0]; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|         lang: function() { | ||||
|             // Gets the active message catalog language. This is based on what | ||||
|             // locale the editor is using and what languages are available. | ||||
|             // | ||||
|             var preferredLangs = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage()); | ||||
|             var preferredLangs = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages); | ||||
|             var knownLangs = RED.settings.theme("languages")||["en-US"]; | ||||
|             for (var i=0;i<preferredLangs.length;i++) { | ||||
|                 if (knownLangs.indexOf(preferredLangs[i]) > -1) { | ||||
|                     return preferredLangs[i] | ||||
|                 } | ||||
|             } | ||||
|             return 'end-US' | ||||
|             return 'en-US' | ||||
|         }, | ||||
|         loadNodeCatalog: function(namespace,done) { | ||||
|             var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage()); | ||||
|             var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages); | ||||
|             var toLoad = languageList.length; | ||||
|             languageList.forEach(function(lang) { | ||||
|                 $.ajax({ | ||||
| @@ -74,7 +90,7 @@ RED.i18n = (function() { | ||||
|                     cache: false, | ||||
|                     url: apiRootUrl+'nodes/'+namespace+'/messages?lng='+lang, | ||||
|                     success: function(data) { | ||||
|                         i18n.addResourceBundle(lang,namespace,data); | ||||
|                         i18next.addResourceBundle(lang,namespace,data); | ||||
|                         toLoad--; | ||||
|                         if (toLoad === 0) { | ||||
|                             done(); | ||||
| @@ -86,7 +102,7 @@ RED.i18n = (function() { | ||||
|         }, | ||||
|  | ||||
|         loadNodeCatalogs: function(done) { | ||||
|             var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage()); | ||||
|             var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages); | ||||
|             var toLoad = languageList.length; | ||||
|  | ||||
|             languageList.forEach(function(lang) { | ||||
| @@ -99,7 +115,7 @@ RED.i18n = (function() { | ||||
|                     success: function(data) { | ||||
|                         var namespaces = Object.keys(data); | ||||
|                         namespaces.forEach(function(ns) { | ||||
|                             i18n.addResourceBundle(lang,ns,data[ns]); | ||||
|                             i18next.addResourceBundle(lang,ns,data[ns]); | ||||
|                         }); | ||||
|                         toLoad--; | ||||
|                         if (toLoad === 0) { | ||||
| @@ -111,7 +127,7 @@ RED.i18n = (function() { | ||||
|         }, | ||||
|  | ||||
|         loadPluginCatalogs: function(done) { | ||||
|             var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage()); | ||||
|             var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages); | ||||
|             var toLoad = languageList.length; | ||||
|  | ||||
|             languageList.forEach(function(lang) { | ||||
| @@ -124,7 +140,7 @@ RED.i18n = (function() { | ||||
|                     success: function(data) { | ||||
|                         var namespaces = Object.keys(data); | ||||
|                         namespaces.forEach(function(ns) { | ||||
|                             i18n.addResourceBundle(lang,ns,data[ns]); | ||||
|                             i18next.addResourceBundle(lang,ns,data[ns]); | ||||
|                         }); | ||||
|                         toLoad--; | ||||
|                         if (toLoad === 0) { | ||||
| @@ -133,6 +149,7 @@ RED.i18n = (function() { | ||||
|                     } | ||||
|                 }); | ||||
|             }) | ||||
|         } | ||||
|         }, | ||||
|         detectLanguage: detectLanguage | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -25,7 +25,9 @@ | ||||
|         "ctrl-alt-o": "core:open-project", | ||||
|         "ctrl-g v": "core:show-version-control-tab", | ||||
|         "ctrl-shift-l": "core:show-event-log", | ||||
|         "ctrl-shift-p":"core:show-action-list" | ||||
|         "ctrl-shift-p":"core:show-action-list", | ||||
|         "alt-w": "core:hide-flow", | ||||
|         "alt-shift-w": "core:show-last-hidden-flow" | ||||
|     }, | ||||
|     "red-ui-sidebar-node-config": { | ||||
|         "backspace": "core:delete-config-selection", | ||||
| @@ -36,7 +38,9 @@ | ||||
|     }, | ||||
|     "red-ui-workspace": { | ||||
|         "backspace": "core:delete-selection", | ||||
|         "ctrl-backspace": "core:delete-selection-and-reconnect", | ||||
|         "delete": "core:delete-selection", | ||||
|         "ctrl-delete": "core:delete-selection-and-reconnect", | ||||
|         "enter": "core:edit-selected-node", | ||||
|         "ctrl-enter": "core:go-to-selection", | ||||
|         "ctrl-c": "core:copy-selection-to-internal-clipboard", | ||||
| @@ -77,6 +81,19 @@ | ||||
|         "right": "core:go-to-nearest-node-on-right", | ||||
|         "left": "core:go-to-nearest-node-on-left", | ||||
|         "up": "core:go-to-nearest-node-above", | ||||
|         "down": "core:go-to-nearest-node-below" | ||||
|         "down": "core:go-to-nearest-node-below", | ||||
|         "alt-a g": "core:align-selection-to-grid", | ||||
|         "alt-a l": "core:align-selection-to-left", | ||||
|         "alt-a r": "core:align-selection-to-right", | ||||
|         "alt-a t": "core:align-selection-to-top", | ||||
|         "alt-a b": "core:align-selection-to-bottom", | ||||
|         "alt-a m": "core:align-selection-to-middle", | ||||
|         "alt-a c": "core:align-selection-to-center", | ||||
|         "alt-a h": "core:distribute-selection-horizontally", | ||||
|         "alt-a v": "core:distribute-selection-vertically", | ||||
|         "shift-f": "core:search-previous", | ||||
|         "f": "core:search-next", | ||||
|         "alt-l l": "core:split-wire-with-link-nodes" | ||||
|  | ||||
|      } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -36,7 +36,7 @@ var RED = (function() { | ||||
|     } | ||||
|     function loadPlugins(done) { | ||||
|         loader.reportProgress(RED._("event.loadPlugins",{count:""}), 17) | ||||
|         var lang = localStorage.getItem("editor-language")||i18n.detectLanguage(); | ||||
|         var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage(); | ||||
|  | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
| @@ -176,7 +176,7 @@ var RED = (function() { | ||||
|  | ||||
|     function loadNodes() { | ||||
|         loader.reportProgress(RED._("event.loadNodes",{count:""}), 30) | ||||
|         var lang = localStorage.getItem("editor-language")||i18n.detectLanguage(); | ||||
|         var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage(); | ||||
|  | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
| @@ -201,6 +201,7 @@ var RED = (function() { | ||||
|                             RED.projects.refresh(function(activeProject) { | ||||
|                                 loadFlows(function() { | ||||
|                                     RED.sidebar.info.refresh() | ||||
|                                     var showProjectWelcome = false; | ||||
|                                     if (!activeProject) { | ||||
|                                         // Projects enabled but no active project | ||||
|                                         RED.menu.setDisabled('menu-item-projects-open',true); | ||||
| @@ -208,10 +209,10 @@ var RED = (function() { | ||||
|                                         if (activeProject === false) { | ||||
|                                             // User previously decline the migration to projects. | ||||
|                                         } else { // null/undefined | ||||
|                                             RED.projects.showStartup(); | ||||
|                                             showProjectWelcome = true; | ||||
|                                         } | ||||
|                                     } | ||||
|                                     completeLoad(); | ||||
|                                     completeLoad(showProjectWelcome); | ||||
|                                 }); | ||||
|                             }); | ||||
|                         } else { | ||||
| @@ -251,6 +252,22 @@ var RED = (function() { | ||||
|                         if (/^#flow\/.+$/.test(currentHash)) { | ||||
|                             RED.workspaces.show(currentHash.substring(6),true); | ||||
|                         } | ||||
|                         if (RED.workspaces.count() > 0) { | ||||
|                             const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}"); | ||||
|                             const workspaces = RED.nodes.getWorkspaceOrder(); | ||||
|                             if (RED.workspaces.active() === 0) { | ||||
|                                 for (let index = 0; index < workspaces.length; index++) { | ||||
|                                     const ws = workspaces[index]; | ||||
|                                     if (!hiddenTabs[ws]) { | ||||
|                                         RED.workspaces.show(ws); | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             if (RED.workspaces.active() === 0) { | ||||
|                                 RED.workspaces.show(workspaces[0]); | ||||
|                             } | ||||
|                         } | ||||
|                     } catch(err) { | ||||
|                         console.warn(err); | ||||
|                         RED.notify( | ||||
| @@ -267,7 +284,7 @@ var RED = (function() { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function completeLoad() { | ||||
|     function completeLoad(showProjectWelcome) { | ||||
|         var persistentNotifications = {}; | ||||
|         RED.comms.subscribe("notification/#",function(topic,msg) { | ||||
|             var parts = topic.split("/"); | ||||
| @@ -341,6 +358,14 @@ var RED = (function() { | ||||
|                         } else { | ||||
|                             options.buttons = [ | ||||
|                                 { | ||||
|                                     text: RED._("notification.label.unknownNodesButton"), | ||||
|                                     class: "pull-left", | ||||
|                                     click: function() { | ||||
|                                         RED.actions.invoke("core:search", "type:unknown "); | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     class: "primary", | ||||
|                                     text: RED._("common.label.close"), | ||||
|                                     click: function() { | ||||
|                                         persistentNotifications[notificationId].hideNotification(); | ||||
| @@ -457,7 +482,7 @@ var RED = (function() { | ||||
|             var parts = topic.split("/"); | ||||
|             var node = RED.nodes.node(parts[1]); | ||||
|             if (node) { | ||||
|                 if (msg.hasOwnProperty("text") && msg.text !== null && /^[a-zA-Z]/.test(msg.text)) { | ||||
|                 if (msg.hasOwnProperty("text") && msg.text !== null && /^[@a-zA-Z]/.test(msg.text)) { | ||||
|                     msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()}); | ||||
|                 } | ||||
|                 node.status = msg; | ||||
| @@ -471,22 +496,33 @@ var RED = (function() { | ||||
|             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.loadNodeCatalog(id, function() { | ||||
|                         $.get('nodes/'+id, function(data) { | ||||
|                             appendNodeConfig(data); | ||||
|                 RED.settings.refreshSettings(function(err, data) { | ||||
|                     var addedTypes = []; | ||||
|                     msg.forEach(function(m) { | ||||
|                         var id = m.id; | ||||
|                         RED.nodes.addNodeSet(m); | ||||
|                         addedTypes = addedTypes.concat(m.types); | ||||
|                         RED.i18n.loadNodeCatalog(id, function() { | ||||
|                             var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage(); | ||||
|                             $.ajax({ | ||||
|                                 headers: { | ||||
|                                     "Accept":"text/html", | ||||
|                                     "Accept-Language": lang | ||||
|                                 }, | ||||
|                                 cache: false, | ||||
|                                 url: 'nodes/'+id, | ||||
|                                 success: function(data) { | ||||
|                                     appendNodeConfig(data); | ||||
|                                 } | ||||
|                             }); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|                 if (addedTypes.length) { | ||||
|                     typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                     RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success"); | ||||
|                 } | ||||
|                 loadIconList(); | ||||
|                     if (addedTypes.length) { | ||||
|                         typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).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]; | ||||
| @@ -499,18 +535,29 @@ var RED = (function() { | ||||
|                 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.map(RED.utils.sanitize).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); | ||||
|                     RED.settings.refreshSettings(function(err, data) { | ||||
|                         info = RED.nodes.getNodeSet(msg.id); | ||||
|                         if (info.added) { | ||||
|                             RED.nodes.enableNodeSet(msg.id); | ||||
|                             typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                             RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success"); | ||||
|                         }); | ||||
|                     } | ||||
|                             RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success"); | ||||
|                         } else { | ||||
|                             var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage(); | ||||
|                             $.ajax({ | ||||
|                                 headers: { | ||||
|                                     "Accept":"text/html", | ||||
|                                     "Accept-Language": lang | ||||
|                                 }, | ||||
|                                 cache: false, | ||||
|                                 url: 'nodes/'+msg.id, | ||||
|                                 success: function(data) { | ||||
|                                     appendNodeConfig(data); | ||||
|                                     typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).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) { | ||||
| @@ -530,22 +577,28 @@ var RED = (function() { | ||||
|  | ||||
|         $(".red-ui-header-toolbar").show(); | ||||
|  | ||||
|  | ||||
|         RED.sidebar.show(":first"); | ||||
|         RED.sidebar.show(":first", true); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|             loader.end(); | ||||
|             checkFirstRun(function() { | ||||
|                 if (showProjectWelcome) { | ||||
|                     RED.projects.showStartup(); | ||||
|                 } | ||||
|             }); | ||||
|         },100); | ||||
|     } | ||||
|  | ||||
|     function showAbout() { | ||||
|         $.get('red/about', function(data) { | ||||
|             var aboutHeader = '<div style="text-align:center;">'+ | ||||
|             '<img width="50px" src="red/images/node-red-icon.svg" />'+ | ||||
|             '</div>'; | ||||
|  | ||||
|             RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data)); | ||||
|         }); | ||||
|     function checkFirstRun(done) { | ||||
|         if (RED.settings.theme("tours") === false) { | ||||
|             done(); | ||||
|             return; | ||||
|         } | ||||
|         if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) { | ||||
|             done(); | ||||
|             return; | ||||
|         } | ||||
|         RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"), done); | ||||
|     } | ||||
|  | ||||
|     function buildMainMenu() { | ||||
| @@ -557,6 +610,25 @@ var RED = (function() { | ||||
|                 {id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"} | ||||
|             ]}); | ||||
|         } | ||||
|         menuOptions.push({id:"menu-item-edit-menu", label:RED._("menu.label.edit"), options: [ | ||||
|             {id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"}, | ||||
|             {id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"}, | ||||
|             null, | ||||
|             {id: "menu-item-edit-cut", label:RED._("keyboard.cutNode"), onselect: "core:cut-selection-to-internal-clipboard"}, | ||||
|             {id: "menu-item-edit-copy", label:RED._("keyboard.copyNode"), onselect: "core:copy-selection-to-internal-clipboard"}, | ||||
|             {id: "menu-item-edit-paste", label:RED._("keyboard.pasteNode"), disabled: true, onselect: "core:paste-from-internal-clipboard"}, | ||||
|             null, | ||||
|             {id: "menu-item-edit-copy-group-style", label:RED._("keyboard.copyGroupStyle"), onselect: "core:copy-group-style"}, | ||||
|             {id: "menu-item-edit-paste-group-style", label:RED._("keyboard.pasteGroupStyle"), disabled: true, onselect: "core:paste-group-style"}, | ||||
|             null, | ||||
|             {id: "menu-item-edit-select-all", label:RED._("keyboard.selectAll"), onselect: "core:select-all-nodes"}, | ||||
|             {id: "menu-item-edit-select-connected", label:RED._("keyboard.selectAllConnected"), onselect: "core:select-connected-nodes"}, | ||||
|             {id: "menu-item-edit-select-none", label:RED._("keyboard.selectNone"), onselect: "core:select-none"}, | ||||
|             null, | ||||
|             {id: "menu-item-edit-split-wire-with-links", label:RED._("keyboard.splitWireWithLinks"), onselect: "core:split-wire-with-link-nodes"}, | ||||
|  | ||||
|         ]}); | ||||
|  | ||||
|         menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[ | ||||
|             {id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true}, | ||||
|             {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true}, | ||||
| @@ -564,6 +636,25 @@ var RED = (function() { | ||||
|             {id:"menu-item-action-list",label:RED._("keyboard.actionList"),onselect:"core:show-action-list"}, | ||||
|             null | ||||
|         ]}); | ||||
|  | ||||
|         menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [ | ||||
|             {id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"}, | ||||
|             {id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"}, | ||||
|             {id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"}, | ||||
|             {id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"}, | ||||
|             null, | ||||
|             {id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"}, | ||||
|             {id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"}, | ||||
|             {id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"}, | ||||
|             null, | ||||
|             {id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), disabled: true, onselect: "core:align-selection-to-top"}, | ||||
|             {id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), disabled: true, onselect: "core:align-selection-to-middle"}, | ||||
|             {id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"}, | ||||
|             null, | ||||
|             {id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"}, | ||||
|             {id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"} | ||||
|         ]}); | ||||
|  | ||||
|         menuOptions.push(null); | ||||
|         if (RED.settings.theme("menu.menu-item-import-library", true)) { | ||||
|             menuOptions.push({id: "menu-item-import", label: RED._("menu.label.import"), onselect: "core:show-import-dialog"}); | ||||
| @@ -624,7 +715,6 @@ var RED = (function() { | ||||
|         RED.user.init(); | ||||
|         RED.notifications.init(); | ||||
|         RED.library.init(); | ||||
|         RED.keyboard.init(); | ||||
|         RED.palette.init(); | ||||
|         RED.eventLog.init(); | ||||
|  | ||||
| @@ -648,21 +738,19 @@ var RED = (function() { | ||||
|         RED.search.init(); | ||||
|         RED.actionList.init(); | ||||
|         RED.editor.init(); | ||||
|         RED.diagnostics.init(); | ||||
|         RED.diff.init(); | ||||
|  | ||||
|  | ||||
|         RED.deploy.init(RED.settings.theme("deployButton",null)); | ||||
|  | ||||
|         buildMainMenu(); | ||||
|         RED.keyboard.init(buildMainMenu); | ||||
|  | ||||
|         RED.nodes.init(); | ||||
|         RED.comms.connect(); | ||||
|  | ||||
|         $("#red-ui-main-container").show(); | ||||
|  | ||||
|  | ||||
|         RED.actions.add("core:show-about", showAbout); | ||||
|  | ||||
|         loadPluginList(); | ||||
|     } | ||||
|  | ||||
| @@ -711,7 +799,7 @@ var RED = (function() { | ||||
|             throw new Error("RED already initialised"); | ||||
|         } | ||||
|         initialised = true; | ||||
|         ace.require("ace/ext/language_tools"); | ||||
|         if(window.ace) { window.ace.require("ace/ext/language_tools"); } | ||||
|         options = options || {}; | ||||
|         options.apiRootUrl = options.apiRootUrl || ""; | ||||
|         if (options.apiRootUrl && !/\/$/.test(options.apiRootUrl)) { | ||||
|   | ||||
| @@ -19,7 +19,6 @@ RED.settings = (function () { | ||||
|  | ||||
|     var loadedSettings = {}; | ||||
|     var userSettings = {}; | ||||
|     var settingsDirty = false; | ||||
|     var pendingSave; | ||||
|  | ||||
|     var hasLocalStorage = function () { | ||||
| @@ -126,7 +125,7 @@ RED.settings = (function () { | ||||
|         load(done); | ||||
|     } | ||||
|  | ||||
|     var load = function(done) { | ||||
|     var refreshSettings = function(done) { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept": "application/json" | ||||
| @@ -136,6 +135,23 @@ RED.settings = (function () { | ||||
|             url: 'settings', | ||||
|             success: function (data) { | ||||
|                 setProperties(data); | ||||
|                 done(null, data); | ||||
|             }, | ||||
|             error: function(jqXHR,textStatus,errorThrown) { | ||||
|                 if (jqXHR.status === 401) { | ||||
|                     if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) { | ||||
|                         window.location.search = ""; | ||||
|                     } | ||||
|                     RED.user.login(function() { refreshSettings(done); }); | ||||
|                 } else { | ||||
|                     console.log("Unexpected error loading settings:",jqXHR.status,textStatus); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     var load = function(done) { | ||||
|         refreshSettings(function(err, data) { | ||||
|             if (!err) { | ||||
|                 if (!RED.settings.user || RED.settings.user.anonymous) { | ||||
|                     RED.settings.remove("auth-tokens"); | ||||
|                 } | ||||
| @@ -143,22 +159,13 @@ RED.settings = (function () { | ||||
|                 console.groupCollapsed("Versions"); | ||||
|                 console.log("jQuery",$().jquery) | ||||
|                 console.log("jQuery UI",$.ui.version); | ||||
|                 console.log("ACE",ace.version); | ||||
|                 if(window.ace) { console.log("ACE",ace.version); } | ||||
|                 if(window.monaco) { console.log("MONACO",monaco.version || "unknown"); } | ||||
|                 console.log("D3",d3.version); | ||||
|                 console.groupEnd(); | ||||
|                 loadUserSettings(done); | ||||
|             }, | ||||
|             error: function(jqXHR,textStatus,errorThrown) { | ||||
|                 if (jqXHR.status === 401) { | ||||
|                     if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) { | ||||
|                         window.location.search = ""; | ||||
|                     } | ||||
|                     RED.user.login(function() { load(done); }); | ||||
|                 } else { | ||||
|                     console.log("Unexpected error loading settings:",jqXHR.status,textStatus); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     function loadUserSettings(done) { | ||||
| @@ -219,14 +226,28 @@ RED.settings = (function () { | ||||
|             return defaultValue; | ||||
|         } | ||||
|     } | ||||
|     function getLocal(key) { | ||||
|         return localStorage.getItem(key) | ||||
|     } | ||||
|     function setLocal(key, value) { | ||||
|         localStorage.setItem(key, value); | ||||
|     } | ||||
|     function removeLocal(key) { | ||||
|         localStorage.removeItem(key) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         load: load, | ||||
|         loadUserSettings: loadUserSettings, | ||||
|         refreshSettings: refreshSettings, | ||||
|         set: set, | ||||
|         get: get, | ||||
|         remove: remove, | ||||
|         theme: theme | ||||
|         theme: theme, | ||||
|         setLocal: setLocal, | ||||
|         getLocal: getLocal, | ||||
|         removeLocal: removeLocal | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -160,18 +160,19 @@ RED.actionList = (function() { | ||||
|                 createDialog(); | ||||
|             } | ||||
|             dialog.slideDown(300); | ||||
|             searchInput.searchBox('value',v) | ||||
|             searchInput.searchBox('value',v); | ||||
|             searchResults.editableList('empty'); | ||||
|             results = []; | ||||
|             var actions = RED.actions.list(); | ||||
|             actions.sort(function(A,B) { | ||||
|                 return A.id.localeCompare(B.id); | ||||
|                 var Akey = A.label; | ||||
|                 var Bkey = B.label; | ||||
|                 return Akey.localeCompare(Bkey); | ||||
|             }); | ||||
|             actions.forEach(function(action) { | ||||
|                 action.label = action.id.replace(/:/,": ").replace(/-/g," ").replace(/(^| )./g,function() { return arguments[0].toUpperCase()}); | ||||
|                 action._label = action.label.toLowerCase(); | ||||
|                 searchResults.editableList('addItem',action) | ||||
|             }) | ||||
|                 searchResults.editableList('addItem',action); | ||||
|             }); | ||||
|             RED.events.emit("actionList:open"); | ||||
|             visible = true; | ||||
|         } | ||||
|   | ||||
| @@ -1,25 +1,39 @@ | ||||
| RED.actions = (function() { | ||||
|     var actions = { | ||||
|  | ||||
|     } | ||||
|     }; | ||||
|  | ||||
|     function addAction(name,handler) { | ||||
|         actions[name] = handler; | ||||
|     function addAction(name,handler,options) { | ||||
|         if (typeof handler !== 'function') { | ||||
|             throw new Error("Action handler not a function"); | ||||
|         } | ||||
|         if (actions[name]) { | ||||
|             throw new Error("Cannot override existing action"); | ||||
|         } | ||||
|         actions[name] = { | ||||
|             handler: handler, | ||||
|             options: options, | ||||
|         }; | ||||
|     } | ||||
|     function removeAction(name) { | ||||
|         delete actions[name]; | ||||
|     } | ||||
|     function getAction(name) { | ||||
|         return actions[name]; | ||||
|         return actions[name].handler; | ||||
|     } | ||||
|     function invokeAction(name,args) { | ||||
|     function invokeAction() { | ||||
|         var args = Array.prototype.slice.call(arguments); | ||||
|         var name = args.shift(); | ||||
|         if (actions.hasOwnProperty(name)) { | ||||
|             actions[name](args); | ||||
|             var handler = actions[name].handler; | ||||
|             handler.apply(null, args); | ||||
|         } | ||||
|     } | ||||
|     function listActions() { | ||||
|         var result = []; | ||||
|         var missing = []; | ||||
|         Object.keys(actions).forEach(function(action) { | ||||
|             var def = actions[action]; | ||||
|             var shortcut = RED.keyboard.getShortcut(action); | ||||
|             var isUser = false; | ||||
|             if (shortcut) { | ||||
| @@ -27,13 +41,38 @@ RED.actions = (function() { | ||||
|             } else { | ||||
|                 isUser = !!RED.keyboard.getUserShortcut(action); | ||||
|             } | ||||
|             if (!def.label) { | ||||
|                 var name = action; | ||||
|                 var options = def.options; | ||||
|                 var key = options ? options.label : undefined; | ||||
|                 if (!key) { | ||||
|                     key = "action-list." +name.replace(/^.*:/,""); | ||||
|                 } | ||||
|                 var label = RED._(key); | ||||
|                 if (label === key) { | ||||
|                     // no translation. convert `name` to description  | ||||
|                     label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() { | ||||
|                         if (arguments[5] === 0) { | ||||
|                             return arguments[2].toUpperCase(); | ||||
|                         } else { | ||||
|                             return " "+arguments[4].toUpperCase(); | ||||
|                         } | ||||
|                     }); | ||||
|                     missing.push(key); | ||||
|                 } | ||||
|                 def.label = label; | ||||
|             } | ||||
|             //console.log("; missing:", missing); | ||||
|  | ||||
|             result.push({ | ||||
|                 id:action, | ||||
|                 scope:shortcut?shortcut.scope:undefined, | ||||
|                 key:shortcut?shortcut.key:undefined, | ||||
|                 user:isUser | ||||
|             }) | ||||
|         }) | ||||
|                 user:isUser, | ||||
|                 label: def.label, | ||||
|                 options: def.options, | ||||
|             }); | ||||
|         }); | ||||
|         return result; | ||||
|     } | ||||
|     return { | ||||
|   | ||||
| @@ -71,6 +71,7 @@ RED.clipboard = (function() { | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                             RED.view.focus(); | ||||
|                         } | ||||
|                     }, | ||||
|                     { // red-ui-clipboard-dialog-download | ||||
| @@ -81,6 +82,7 @@ RED.clipboard = (function() { | ||||
|                             var data = $("#red-ui-clipboard-dialog-export-text").val(); | ||||
|                             downloadData("flows.json", data); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                             RED.view.focus(); | ||||
|                         } | ||||
|                     }, | ||||
|                     { // red-ui-clipboard-dialog-export | ||||
| @@ -95,6 +97,7 @@ RED.clipboard = (function() { | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                                 copyText(flowData); | ||||
|                                 RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"}); | ||||
|                                 RED.view.focus(); | ||||
|                             } else { | ||||
|                                 var flowToExport = $("#red-ui-clipboard-dialog-export-text").val(); | ||||
|                                 var selectedPath = activeLibraries[activeTab].getSelected(); | ||||
| @@ -110,6 +113,7 @@ RED.clipboard = (function() { | ||||
|                                         contentType: "application/json; charset=utf-8" | ||||
|                                     }).done(function() { | ||||
|                                         $(dialog).dialog( "close" ); | ||||
|                                         RED.view.focus(); | ||||
|                                         RED.notify(RED._("library.exportedToLibrary"),"success"); | ||||
|                                     }).fail(function(xhr,textStatus,err) { | ||||
|                                         if (xhr.status === 401) { | ||||
| @@ -171,6 +175,7 @@ RED.clipboard = (function() { | ||||
|                                 } | ||||
|                             } | ||||
|                             $( this ).dialog( "close" ); | ||||
|                             RED.view.focus(); | ||||
|                         } | ||||
|                     }, | ||||
|                     { // red-ui-clipboard-dialog-import-conflict | ||||
| @@ -203,6 +208,7 @@ RED.clipboard = (function() { | ||||
|                             // console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}})) | ||||
|                             RED.view.importNodes(newNodes, pendingImportConfig.importOptions); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                             RED.view.focus(); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
| @@ -452,7 +458,7 @@ RED.clipboard = (function() { | ||||
|  | ||||
|         var libraries = RED.settings.libraries || []; | ||||
|         libraries.forEach(function(lib) { | ||||
|             var tabId = "red-ui-clipboard-dialog-import-tab-library-"+lib.id | ||||
|             var tabId = "red-ui-clipboard-dialog-import-tab-"+lib.id | ||||
|             tabs.addTab({ | ||||
|                 id: tabId, | ||||
|                 label: RED._(lib.label||lib.id) | ||||
| @@ -498,6 +504,13 @@ RED.clipboard = (function() { | ||||
|         $("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport); | ||||
|         $("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)}); | ||||
|  | ||||
|         if (RED.workspaces.active() === 0) { | ||||
|             $("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected"); | ||||
|             $("#red-ui-clipboard-dialog-import-opt-new").addClass("selected"); | ||||
|         } else { | ||||
|             $("#red-ui-clipboard-dialog-import-opt-current").removeClass('disabled').addClass("selected"); | ||||
|             $("#red-ui-clipboard-dialog-import-opt-new").removeClass("selected"); | ||||
|         } | ||||
|         $("#red-ui-clipboard-dialog-import-opt > a").on("click", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             if ($(this).hasClass('disabled') || $(this).hasClass('selected')) { | ||||
| @@ -611,9 +624,6 @@ RED.clipboard = (function() { | ||||
|             activeLibraries[tabId] = browser; | ||||
|         }) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         $("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename); | ||||
|         $("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)}); | ||||
|         $("#red-ui-clipboard-dialog-export").button("enable"); | ||||
| @@ -636,7 +646,6 @@ RED.clipboard = (function() { | ||||
|             label: RED._("editor.types.json") | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({ | ||||
|             data: [] | ||||
|         }) | ||||
| @@ -701,6 +710,13 @@ RED.clipboard = (function() { | ||||
|                 var activeWorkspace = RED.workspaces.active(); | ||||
|                 nodes = RED.nodes.groups(activeWorkspace); | ||||
|                 nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace})); | ||||
|                 RED.nodes.eachConfig(function(n) { | ||||
|                     if (n.z === RED.workspaces.active() && n._def.hasUsers === false) { | ||||
|                         // Grab any config nodes scoped to this flow that don't | ||||
|                         // require any flow-nodes to use them | ||||
|                         nodes.push(n); | ||||
|                     } | ||||
|                 }); | ||||
|                 var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace); | ||||
|                 nodes.unshift(parentNode); | ||||
|                 nodes = RED.nodes.createExportableNodeSet(nodes); | ||||
| @@ -731,16 +747,22 @@ RED.clipboard = (function() { | ||||
|         $("#red-ui-clipboard-dialog-export").hide(); | ||||
|         $("#red-ui-clipboard-dialog-import-conflict").hide(); | ||||
|  | ||||
|         var selection = RED.workspaces.selection(); | ||||
|         if (selection.length > 0) { | ||||
|             $("#red-ui-clipboard-dialog-export-rng-selected").trigger("click"); | ||||
|         if (RED.workspaces.active() === 0) { | ||||
|             $("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected'); | ||||
|             $("#red-ui-clipboard-dialog-export-rng-flow").addClass('disabled').removeClass('selected'); | ||||
|             $("#red-ui-clipboard-dialog-export-rng-full").trigger("click"); | ||||
|         } else { | ||||
|             selection = RED.view.selection(); | ||||
|             if (selection.nodes) { | ||||
|             var selection = RED.workspaces.selection(); | ||||
|             if (selection.length > 0) { | ||||
|                 $("#red-ui-clipboard-dialog-export-rng-selected").trigger("click"); | ||||
|             } else { | ||||
|                 $("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected'); | ||||
|                 $("#red-ui-clipboard-dialog-export-rng-flow").trigger("click"); | ||||
|                 selection = RED.view.selection(); | ||||
|                 if (selection.nodes) { | ||||
|                     $("#red-ui-clipboard-dialog-export-rng-selected").trigger("click"); | ||||
|                 } else { | ||||
|                     $("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected'); | ||||
|                     $("#red-ui-clipboard-dialog-export-rng-flow").trigger("click"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (format === "red-ui-clipboard-dialog-export-fmt-full") { | ||||
| @@ -924,7 +946,8 @@ RED.clipboard = (function() { | ||||
|         if (truncated) { | ||||
|             msg += "_truncated"; | ||||
|         } | ||||
|         $("#red-ui-clipboard-hidden").val(value).focus().select(); | ||||
|         var clipboardHidden = $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo(document.body); | ||||
|         clipboardHidden.val(value).focus().select(); | ||||
|         var result =  document.execCommand("copy"); | ||||
|         if (result && element) { | ||||
|             var popover = RED.popover.create({ | ||||
| @@ -938,14 +961,13 @@ RED.clipboard = (function() { | ||||
|             },1000); | ||||
|             popover.open(); | ||||
|         } | ||||
|         $("#red-ui-clipboard-hidden").val(""); | ||||
|         clipboardHidden.remove(); | ||||
|         if (currentFocus) { | ||||
|             $(currentFocus).focus(); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function importNodes(nodesStr,addFlow) { | ||||
|         var newNodes = nodesStr; | ||||
|         if (typeof nodesStr === 'string') { | ||||
| @@ -965,6 +987,7 @@ RED.clipboard = (function() { | ||||
|         try { | ||||
|             RED.view.importNodes(newNodes, importOptions); | ||||
|         } catch(error) { | ||||
|             console.log(error.importConfig) | ||||
|             // Thrown for import_conflict | ||||
|             confirmImport(error.importConfig, newNodes, importOptions); | ||||
|         } | ||||
| @@ -1186,22 +1209,6 @@ RED.clipboard = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function getNodeLabelText(n) { | ||||
|         var label = n.name || n.type+": "+n.id; | ||||
|         if (n._def.label) { | ||||
|             try { | ||||
|                 label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||""; | ||||
|             } catch(err) { | ||||
|                 console.log("Definition error: "+n.type+".label",err); | ||||
|             } | ||||
|         } | ||||
|         var newlineIndex = label.indexOf("\\n"); | ||||
|         if (newlineIndex > -1) { | ||||
|             label = label.substring(0,newlineIndex)+"..."; | ||||
|         } | ||||
|         return label; | ||||
|     } | ||||
|  | ||||
|     function getFlowLabel(n) { | ||||
|         n = JSON.parse(JSON.stringify(n)); | ||||
|         n._def = RED.nodes.getType(n.type) || {}; | ||||
| @@ -1227,16 +1234,8 @@ RED.clipboard = (function() { | ||||
|         if (n._def) { | ||||
|             n._ = n._def._; | ||||
|         } | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item"}); | ||||
|         RED.utils.createNodeIcon(n).appendTo(div); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); | ||||
|         var labelText = getNodeLabelText(n); | ||||
|         var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); | ||||
|         if (labelText) { | ||||
|             label.text(labelText) | ||||
|         } else { | ||||
|             label.html(n.type) | ||||
|         } | ||||
|         var div = $('<div>',{class:"red-ui-node-list-item"}); | ||||
|         RED.utils.createNodeIcon(n,true).appendTo(div); | ||||
|         return div; | ||||
|     } | ||||
|  | ||||
| @@ -1244,8 +1243,6 @@ RED.clipboard = (function() { | ||||
|         init: function() { | ||||
|             setupDialogs(); | ||||
|  | ||||
|             $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor"); | ||||
|  | ||||
|             RED.actions.add("core:show-export-dialog",showExportNodes); | ||||
|             RED.actions.add("core:show-import-dialog",showImportNodes); | ||||
|  | ||||
| @@ -1284,22 +1281,27 @@ RED.clipboard = (function() { | ||||
|                 hideDropTarget(); | ||||
|             }) | ||||
|             .on("drop",function(event) { | ||||
|                 if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                     var data = event.originalEvent.dataTransfer.getData("text/plain"); | ||||
|                     data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1); | ||||
|                     importNodes(data); | ||||
|                 } else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                     var files = event.originalEvent.dataTransfer.files; | ||||
|                     if (files.length === 1) { | ||||
|                         var file = files[0]; | ||||
|                         var reader = new FileReader(); | ||||
|                         reader.onload = (function(theFile) { | ||||
|                             return function(e) { | ||||
|                                 importNodes(e.target.result); | ||||
|                             }; | ||||
|                         })(file); | ||||
|                         reader.readAsText(file); | ||||
|                 try { | ||||
|                     if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                         var data = event.originalEvent.dataTransfer.getData("text/plain"); | ||||
|                         data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1); | ||||
|                         importNodes(data); | ||||
|                     } else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                         var files = event.originalEvent.dataTransfer.files; | ||||
|                         if (files.length === 1) { | ||||
|                             var file = files[0]; | ||||
|                             var reader = new FileReader(); | ||||
|                             reader.onload = (function(theFile) { | ||||
|                                 return function(e) { | ||||
|                                     importNodes(e.target.result); | ||||
|                                 }; | ||||
|                             })(file); | ||||
|                             reader.readAsText(file); | ||||
|                         } | ||||
|                     } | ||||
|                 } catch(err) { | ||||
|                     // Ensure any errors throw above doesn't stop the drop target from | ||||
|                     // being hidden. | ||||
|                 } | ||||
|                 hideDropTarget(); | ||||
|                 event.preventDefault(); | ||||
|   | ||||
							
								
								
									
										126
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| (function($) { | ||||
|  | ||||
| /** | ||||
|  * Attach to an <input type="text"> to provide auto-complete | ||||
|  * | ||||
|  * $("#node-red-text").autoComplete({ | ||||
|  *     search: function(value) { return ['a','b','c'] } | ||||
|  * }) | ||||
|  * | ||||
|  * options: | ||||
|  * | ||||
|  *  search:    function(value, [done]) | ||||
|  *             A function that is passed the current contents of the input whenever | ||||
|  *             it changes. | ||||
|  *             The function must either return auto-complete options, or pass them | ||||
|  *             to the optional 'done' parameter. | ||||
|  *             If the function signature includes 'done', it must be used | ||||
|  *  minLength: number | ||||
|  *             If `minLength` is 0, pressing down arrow will show the list | ||||
|  * | ||||
|  * The auto-complete options should be an array of objects in the form: | ||||
|  *  { | ||||
|  *      value: String : the value to insert if selected | ||||
|  *      label: String|DOM Element : the label to display in the dropdown. | ||||
|  *  } | ||||
|  * | ||||
|  */ | ||||
|  | ||||
|     $.widget( "nodered.autoComplete", { | ||||
|         _create: function() { | ||||
|             const that = this; | ||||
|             this.completionMenuShown = false; | ||||
|             this.options.minLength = parseInteger(this.options.minLength, 1, 0); | ||||
|             this.options.search = this.options.search || function() { return [] }; | ||||
|             this.element.addClass("red-ui-autoComplete"); | ||||
|             this.element.on("keydown.red-ui-autoComplete", function(evt) { | ||||
|                 if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) { | ||||
|                     var opts = that.menu.options(); | ||||
|                     that.element.val(opts[0].value); | ||||
|                     that.menu.hide(); | ||||
|                     evt.preventDefault(); | ||||
|                 } | ||||
|             }) | ||||
|             this.element.on("keyup.red-ui-autoComplete", function(evt) { | ||||
|                 if (evt.keyCode === 13 || evt.keyCode === 9 || evt.keyCode === 27) { | ||||
|                     // ENTER / TAB / ESCAPE | ||||
|                     return | ||||
|                 } | ||||
|                 if (evt.keyCode === 8 || evt.keyCode === 46) { | ||||
|                     // Delete/Backspace | ||||
|                     if (!that.completionMenuShown) { | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|                 that._updateCompletions(this.value); | ||||
|             }); | ||||
|         }, | ||||
|         _showCompletionMenu: function(completions) { | ||||
|             if (this.completionMenuShown) { | ||||
|                 return; | ||||
|             } | ||||
|             this.menu = RED.popover.menu({ | ||||
|                 tabSelect: true, | ||||
|                 width: 300, | ||||
|                 maxHeight: 200, | ||||
|                 class: "red-ui-autoComplete-container", | ||||
|                 options: completions, | ||||
|                 onselect: (opt) => { this.element.val(opt.value); this.element.focus(); this.element.trigger("change") }, | ||||
|                 onclose: () => { this.completionMenuShown = false; delete this.menu; this.element.focus()} | ||||
|             }); | ||||
|             this.menu.show({ | ||||
|                 target: this.element | ||||
|             }) | ||||
|             this.completionMenuShown = true; | ||||
|         }, | ||||
|         _updateCompletions: function(val) { | ||||
|             const that = this; | ||||
|             if (val.trim().length < this.options.minLength) { | ||||
|                 if (this.completionMenuShown) { | ||||
|                     this.menu.hide(); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|             function displayResults(completions,requestId) { | ||||
|                 if (requestId && requestId !== that.pendingRequest) { | ||||
|                     // This request has been superseded | ||||
|                     return | ||||
|                 } | ||||
|                 if (!completions || completions.length === 0) { | ||||
|                     if (that.completionMenuShown) { | ||||
|                         that.menu.hide(); | ||||
|                     } | ||||
|                     return | ||||
|                 } | ||||
|                 if (that.completionMenuShown) { | ||||
|                     that.menu.options(completions); | ||||
|                 } else { | ||||
|                     that._showCompletionMenu(completions); | ||||
|                 } | ||||
|             } | ||||
|             if (this.options.search.length === 2) { | ||||
|                 const requestId = 1+Math.floor(Math.random()*10000); | ||||
|                 this.pendingRequest = requestId; | ||||
|                 this.options.search(val,function(completions) { displayResults(completions,requestId);}) | ||||
|             } else { | ||||
|                 displayResults(this.options.search(val)) | ||||
|             } | ||||
|         }, | ||||
|         _destroy: function() { | ||||
|             this.element.removeClass("red-ui-autoComplete") | ||||
|             this.element.off("keydown.red-ui-autoComplete") | ||||
|             this.element.off("keyup.red-ui-autoComplete") | ||||
|             if (this.completionMenuShown) { | ||||
|                 this.menu.hide(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|     function parseInteger(input, def, min, max) { | ||||
|         if(input == null) { return (def || 0); } | ||||
|         min = min == null ? Number.NEGATIVE_INFINITY : min;  | ||||
|         max = max == null ? Number.POSITIVE_INFINITY : max;  | ||||
|         let n = parseInt(input); | ||||
|         if(isNaN(n) || n < min || n > max) { n = def || 0; } | ||||
|         return n; | ||||
|     } | ||||
| })(jQuery); | ||||
| @@ -18,7 +18,7 @@ | ||||
| /** | ||||
|  * options: | ||||
|  *   - addButton : boolean|string - text for add label, default 'add' | ||||
|  *   - buttons : array - list of custom buttons (objects with fields 'label', 'icon', 'title', 'click') | ||||
|  *   - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click') | ||||
|  *   - height : number|'auto' | ||||
|  *   - resize : function - called when list as a whole is resized | ||||
|  *   - resizeItem : function(item) - called to resize individual item | ||||
| @@ -94,7 +94,7 @@ | ||||
|             } | ||||
|  | ||||
|             buttons.forEach(function(button) { | ||||
|                 var element = $('<a href="#" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></a>') | ||||
|                 var element = $('<button type="button" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></button>') | ||||
|                     .appendTo(that.topContainer) | ||||
|                     .on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
| @@ -103,6 +103,9 @@ | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
|                 if (button.id) { | ||||
|                     element.attr("id", button.id); | ||||
|                 } | ||||
|                 if (button.title) { | ||||
|                     element.attr("title", button.title); | ||||
|                 } | ||||
|   | ||||
| @@ -82,12 +82,19 @@ RED.menu = (function() { | ||||
|                 linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+opt.label+'</span>'+ | ||||
|                                '<span class="red-ui-menu-sublabel">'+opt.sublabel+'</span></span>' | ||||
|             } else { | ||||
|                 linkContent += '<span class="red-ui-menu-label">'+opt.label+'</span>' | ||||
|                 linkContent += '<span class="red-ui-menu-label"><span>'+opt.label+'</span></span>' | ||||
|             } | ||||
|  | ||||
|             linkContent += '</a>'; | ||||
|  | ||||
|             var link = $(linkContent).appendTo(item); | ||||
|             opt.link = link; | ||||
|             if (typeof opt.onselect === 'string') { | ||||
|                 var shortcut = RED.keyboard.getShortcut(opt.onselect); | ||||
|                 if (shortcut && shortcut.key) { | ||||
|                     opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(link.find(".red-ui-menu-label")); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             menuItems[opt.id] = opt; | ||||
|  | ||||
| @@ -276,6 +283,22 @@ RED.menu = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function refreshShortcuts() { | ||||
|         for (var id in menuItems) { | ||||
|             if (menuItems.hasOwnProperty(id)) { | ||||
|                 var opt = menuItems[id]; | ||||
|                 if (typeof opt.onselect === "string" && opt.shortcutSpan) { | ||||
|                     opt.shortcutSpan.remove(); | ||||
|                     delete opt.shortcutSpan; | ||||
|                     var shortcut = RED.keyboard.getShortcut(opt.onselect); | ||||
|                     if (shortcut && shortcut.key) { | ||||
|                         opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(opt.link.find(".red-ui-menu-label")); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: createMenu, | ||||
|         setSelected: setSelected, | ||||
| @@ -284,6 +307,7 @@ RED.menu = (function() { | ||||
|         setDisabled: setDisabled, | ||||
|         addItem: addItem, | ||||
|         removeItem: removeItem, | ||||
|         setAction: setAction | ||||
|         setAction: setAction, | ||||
|         refreshShortcuts: refreshShortcuts | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -13,24 +13,138 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| /* | ||||
|  * RED.popover.create(options) - create a popover callout box | ||||
|  * RED.popover.tooltip(target,content, action) - add a tooltip to an element | ||||
|  * RED.popover.menu(options) - create a dropdown menu | ||||
|  * RED.popover.panel(content) - create a dropdown container element | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * RED.popover.create(options) | ||||
|  * | ||||
|  *  options | ||||
|  *    - target : DOM element - the element to target with the popover | ||||
|  *    - direction : string - position of the popover relative to target | ||||
|  *                  'top', 'right'(default), 'bottom', 'left', 'inset-[top,right,bottom,left]' | ||||
|  *    - trigger : string - what triggers the popover to be displayed | ||||
|  *                  'hover' - display when hovering the target | ||||
|  *                  'click' - display when target is clicked | ||||
|  *                  'modal' - hmm not sure, need to find where we use that mode | ||||
|  *    - content : string|function - contents of the popover. If a string, handled | ||||
|  *                                  as raw HTML, so take care. | ||||
|  *                                  If a function, can return a String to be added | ||||
|  *                                  as text (not HTML), or a DOM element to append | ||||
|  *    - delay : object - sets show/hide delays after mouseover/out events | ||||
|  *                  { show: 750, hide: 50 } | ||||
|  *    - autoClose : number - delay before closing the popover in some cases | ||||
|  *                     if trigger is click - delay after mouseout | ||||
|  *                     else if trigger not hover/modal - delay after showing | ||||
|  *    - width : number - width of popover, default 'auto' | ||||
|  *    - maxWidth : number - max width of popover, default 'auto' | ||||
|  *    - size : string - scale of popover. 'default', 'small' | ||||
|  *    - offset : number - px offset from target | ||||
|  *    - tooltip : boolean - if true, clicking on popover closes it | ||||
|  *    - class : string - optional css class to apply to popover | ||||
|  *    - interactive : if trigger is 'hover' and this is set to true, allow the mouse | ||||
|  *                    to move over the popover without hiding it. | ||||
|  * | ||||
|  * Returns the popover object with the following properties/functions: | ||||
|  *   properties: | ||||
|  *    - element : DOM element - the popover dom element | ||||
|  *   functions: | ||||
|  *    - setContent(content) - change the popover content. This only works if the | ||||
|  *                            popover is not currently displayed. It does not | ||||
|  *                            change the content of a visible popover. | ||||
|  *    - open(instant) - show the popover. If 'instant' is true, don't fade in | ||||
|  *    - close(instant) - hide the popover. If 'instant' is true, don't fade out | ||||
|  *    - move(options) - move the popover. The options parameter can take many | ||||
|  *                      of the options detailed above including: | ||||
|  *                       target,direction,content,width,offset | ||||
|  *                      Other settings probably won't work because we haven't needed to change them | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * RED.popover.tooltip(target,content, action) | ||||
|  * | ||||
|  *  - target : DOM element - the element to apply the tooltip to | ||||
|  *  - content : string - the text of the tooltip | ||||
|  *  - action : string - *optional* the name of an Action this tooltip is tied to | ||||
|  *                      For example, it 'target' is a button that triggers a particular action. | ||||
|  *                      The tooltip will include the keyboard shortcut for the action | ||||
|  *                      if one is defined | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * RED.popover.menu(options) | ||||
|  * | ||||
|  *  options | ||||
|  *    - options : array - list of menu options - see below for format | ||||
|  *    - width : number - width of the menu. Default: 'auto' | ||||
|  *    - class : string - class to apply to the menu container | ||||
|  *    - maxHeight : number - maximum height of menu before scrolling items. Default: none | ||||
|  *    - onselect : function(item) - called when a menu item is selected, if that item doesn't | ||||
|  *                                  have its own onselect function | ||||
|  *    - onclose : function(cancelled) - called when the menu is closed | ||||
|  *    - disposeOnClose : boolean - by default, the menu is discarded when it closes | ||||
|  *                                 and mustbe rebuilt to redisplay. Setting this to 'false' | ||||
|  *                                 keeps the menu on the DOM so it can be shown again. | ||||
|  * | ||||
|  *  Menu Options array: | ||||
|  *  [ | ||||
|  *      label : string|DOM element - the label of the item. Can be custom DOM element | ||||
|  *      onselect : function - called when the item is selected | ||||
|  *  ] | ||||
|  * | ||||
|  * Returns the menu object with the following functions: | ||||
|  * | ||||
|  *  - options([menuItems]) - if menuItems is undefined, returns the current items. | ||||
|  *                           otherwise, sets the current menu items | ||||
|  *  - show(opts) - shows the menu. `opts` is an object of options. See  RED.popover.panel.show(opts) | ||||
|  *                 for the full list of options. In most scenarios, this just needs: | ||||
|  *                  - target : DOM element - the element to display the menu below | ||||
|  *  - hide(cancelled) - hide the menu | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * RED.popover.panel(content) | ||||
|  *  Create a UI panel that can be displayed relative to any target element. | ||||
|  *  Handles auto-closing when mouse clicks outside the panel | ||||
|  * | ||||
|  *  - 'content' - DOM element to display in the panel | ||||
|  * | ||||
|  * Returns the panel object with the following functions: | ||||
|  * | ||||
|  *  properties: | ||||
|  *    - container : DOM element - the panel element | ||||
|  * | ||||
|  *  functions: | ||||
|  *    - show(opts) - show the panel. | ||||
|  *       opts: | ||||
|  *          - onclose : function - called when the panel closes | ||||
|  *          - closeButton : DOM element - if the panel is closeable by a click of a button, | ||||
|  *                                        by providing a reference to it here, we can | ||||
|  *                                        handle the events properly to hide the panel | ||||
|  *          - target : DOM element - the element to display the panel relative to | ||||
|  *          - align : string - should the panel align to the left or right edge of target | ||||
|  *                             default: 'right' | ||||
|  *          - offset : Array - px offset to apply from the target. [width, height] | ||||
|  *          - dispose : boolean - whether the panel should be removed from DOM when hidden | ||||
|  *                                default: true | ||||
|  *    - hide(dispose) - hide the panel. | ||||
|  */ | ||||
|  | ||||
| RED.popover = (function() { | ||||
|     var deltaSizes = { | ||||
|         "default": { | ||||
|             top: 10, | ||||
|             topTop: 30, | ||||
|             leftRight: 17, | ||||
|             leftLeft: 25, | ||||
|             leftBottom: 8, | ||||
|             leftTop: 11 | ||||
|             x: 12, | ||||
|             y: 12 | ||||
|         }, | ||||
|         "small": { | ||||
|             top: 6, | ||||
|             topTop: 20, | ||||
|             leftRight: 8, | ||||
|             leftLeft: 26, | ||||
|             leftBottom: 8, | ||||
|             leftTop: 9 | ||||
|             x:8, | ||||
|             y:8 | ||||
|         } | ||||
|     } | ||||
|     function createPopover(options) { | ||||
| @@ -41,7 +155,9 @@ RED.popover = (function() { | ||||
|         var delay = options.delay ||  { show: 750, hide: 50 }; | ||||
|         var autoClose = options.autoClose; | ||||
|         var width = options.width||"auto"; | ||||
|         var maxWidth = options.maxWidth; | ||||
|         var size = options.size||"default"; | ||||
|         var popupOffset = options.offset || 0; | ||||
|         if (!deltaSizes[size]) { | ||||
|             throw new Error("Invalid RED.popover size value:",size); | ||||
|         } | ||||
| @@ -49,6 +165,8 @@ RED.popover = (function() { | ||||
|         var timer = null; | ||||
|         var active; | ||||
|         var div; | ||||
|         var contentDiv; | ||||
|         var currentStyle; | ||||
|  | ||||
|         var openPopup = function(instant) { | ||||
|             if (active) { | ||||
| @@ -58,6 +176,10 @@ RED.popover = (function() { | ||||
|                     return; | ||||
|                 } | ||||
|                 div = $('<div class="red-ui-popover"></div>'); | ||||
|                 if (options.class) { | ||||
|                     div.addClass(options.class); | ||||
|                 } | ||||
|                 contentDiv = $('<div class="red-ui-popover-content">').appendTo(div); | ||||
|                 if (size !== "default") { | ||||
|                     div.addClass("red-ui-popover-size-"+size); | ||||
|                 } | ||||
| @@ -67,71 +189,23 @@ RED.popover = (function() { | ||||
|                         return; | ||||
|                     } | ||||
|                     if (typeof result === 'string') { | ||||
|                         div.text(result); | ||||
|                         contentDiv.text(result); | ||||
|                     } else { | ||||
|                         div.append(result); | ||||
|                         contentDiv.append(result); | ||||
|                     } | ||||
|                 } else { | ||||
|                     div.html(content); | ||||
|                 } | ||||
|                 if (width !== "auto") { | ||||
|                     div.width(width); | ||||
|                     contentDiv.html(content); | ||||
|                 } | ||||
|                 div.appendTo("body"); | ||||
|  | ||||
|                 var targetPos = target.offset(); | ||||
|                 var targetWidth = target.outerWidth(); | ||||
|                 var targetHeight = target.outerHeight(); | ||||
|                 var divHeight = div.height(); | ||||
|                 var divWidth = div.width(); | ||||
|                 var paddingRight = 10; | ||||
|                 movePopup({target,direction,width,maxWidth}); | ||||
|  | ||||
|                 var viewportTop = $(window).scrollTop(); | ||||
|                 var viewportLeft = $(window).scrollLeft(); | ||||
|                 var viewportBottom = viewportTop + $(window).height(); | ||||
|                 var viewportRight = viewportLeft + $(window).width(); | ||||
|                 var top = 0; | ||||
|                 var left = 0; | ||||
|                 var d = direction; | ||||
|                 if (d === 'right') { | ||||
|                     top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top; | ||||
|                     left = targetPos.left+targetWidth+deltaSizes[size].leftRight; | ||||
|                 } else if (d === 'left') { | ||||
|                     top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top; | ||||
|                     left = targetPos.left-deltaSizes[size].leftLeft-divWidth; | ||||
|                 } else if (d === 'bottom') { | ||||
|                     top = targetPos.top+targetHeight+deltaSizes[size].top; | ||||
|                     left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom; | ||||
|                     if (left < 0) { | ||||
|                         d = "right"; | ||||
|                         top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top; | ||||
|                         left = targetPos.left+targetWidth+deltaSizes[size].leftRight; | ||||
|                     } else if (left+divWidth+paddingRight > viewportRight) { | ||||
|                         d = "left"; | ||||
|                         top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top; | ||||
|                         left = targetPos.left-deltaSizes[size].leftLeft-divWidth; | ||||
|                         if (top+divHeight+targetHeight/2 + 5 > viewportBottom) { | ||||
|                             top -= (top+divHeight+targetHeight/2 - viewportBottom + 5) | ||||
|                         } | ||||
|                     } else if (top+divHeight > viewportBottom) { | ||||
|                         d = 'top'; | ||||
|                         top = targetPos.top-deltaSizes[size].topTop-divHeight; | ||||
|                         left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop; | ||||
|                     } | ||||
|                 } else if (d === 'top') { | ||||
|                     top = targetPos.top-deltaSizes[size].topTop-divHeight; | ||||
|                     left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop; | ||||
|                     if (top < 0) { | ||||
|                         d = 'bottom'; | ||||
|                         top = targetPos.top+targetHeight+deltaSizes[size].top; | ||||
|                         left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom; | ||||
|                     } | ||||
|                 } | ||||
|                 div.addClass('red-ui-popover-'+d).css({top: top, left: left}); | ||||
|                 if (existingPopover) { | ||||
|                     existingPopover.close(true); | ||||
|                 } | ||||
|                 target.data("red-ui-popover",res) | ||||
|                 if (options.trigger !== 'manual') { | ||||
|                     target.data("red-ui-popover",res) | ||||
|                 } | ||||
|                 if (options.tooltip) { | ||||
|                     div.on("mousedown", function(evt) { | ||||
|                         closePopup(true); | ||||
| @@ -161,6 +235,104 @@ RED.popover = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         var movePopup = function(options) { | ||||
|             target = options.target || target; | ||||
|             direction = options.direction || direction || "right"; | ||||
|             popupOffset = options.offset || popupOffset; | ||||
|             var transition = options.transition; | ||||
|  | ||||
|             var width = options.width||"auto"; | ||||
|             div.width(width); | ||||
|             if (options.maxWidth) { | ||||
|                 div.css("max-width",options.maxWidth) | ||||
|             } else { | ||||
|                 div.css("max-width", 'auto'); | ||||
|             } | ||||
|  | ||||
|             var targetPos = target[0].getBoundingClientRect(); | ||||
|             var targetHeight = targetPos.height; | ||||
|             var targetWidth = targetPos.width; | ||||
|  | ||||
|             var divHeight = div.outerHeight(); | ||||
|             var divWidth = div.outerWidth(); | ||||
|             var paddingRight = 10; | ||||
|  | ||||
|             var viewportTop = $(window).scrollTop(); | ||||
|             var viewportLeft = $(window).scrollLeft(); | ||||
|             var viewportBottom = viewportTop + $(window).height(); | ||||
|             var viewportRight = viewportLeft + $(window).width(); | ||||
|             var top = 0; | ||||
|             var left = 0; | ||||
|             if (direction === 'right') { | ||||
|                 top = targetPos.top+targetHeight/2-divHeight/2; | ||||
|                 left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset; | ||||
|             } else if (direction === 'left') { | ||||
|                 top = targetPos.top+targetHeight/2-divHeight/2; | ||||
|                 left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset; | ||||
|             } else if (direction === 'bottom') { | ||||
|                 top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset; | ||||
|                 left = targetPos.left+targetWidth/2-divWidth/2; | ||||
|                 if (left < 0) { | ||||
|                     direction = "right"; | ||||
|                     top = targetPos.top+targetHeight/2-divHeight/2; | ||||
|                     left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset; | ||||
|                 } else if (left+divWidth+paddingRight > viewportRight) { | ||||
|                     direction = "left"; | ||||
|                     top = targetPos.top+targetHeight/2-divHeight/2; | ||||
|                     left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset; | ||||
|                     if (top+divHeight+targetHeight/2 + 5 > viewportBottom) { | ||||
|                         top -= (top+divHeight+targetHeight/2 - viewportBottom + 5) | ||||
|                     } | ||||
|                 } else if (top+divHeight > viewportBottom) { | ||||
|                     direction = 'top'; | ||||
|                     top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset; | ||||
|                     left = targetPos.left+targetWidth/2-divWidth/2; | ||||
|                 } | ||||
|             } else if (direction === 'top') { | ||||
|                 top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset; | ||||
|                 left = targetPos.left+targetWidth/2-divWidth/2; | ||||
|                 if (top < 0) { | ||||
|                     direction = 'bottom'; | ||||
|                     top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset; | ||||
|                     left = targetPos.left+targetWidth/2-divWidth/2; | ||||
|                 } | ||||
|             } else if (/inset/.test(direction)) { | ||||
|                 top = targetPos.top + targetHeight/2 - divHeight/2; | ||||
|                 left = targetPos.left + targetWidth/2 - divWidth/2; | ||||
|  | ||||
|                 if (/bottom/.test(direction)) { | ||||
|                     top = targetPos.top + targetHeight - divHeight-popupOffset; | ||||
|                 } | ||||
|                 if (/top/.test(direction)) { | ||||
|                     top = targetPos.top+popupOffset; | ||||
|                 } | ||||
|                 if (/left/.test(direction)) { | ||||
|                     left = targetPos.left+popupOffset; | ||||
|                 } | ||||
|                 if (/right/.test(direction)) { | ||||
|                     left = targetPos.left + targetWidth - divWidth-popupOffset; | ||||
|                 } | ||||
|             } | ||||
|             if (currentStyle) { | ||||
|                 div.removeClass(currentStyle); | ||||
|             } | ||||
|             if (transition) { | ||||
|                 div.css({ | ||||
|                     "transition": "0.6s ease", | ||||
|                     "transition-property": "top,left,right,bottom" | ||||
|                 }) | ||||
|             } | ||||
|             currentStyle = 'red-ui-popover-'+direction; | ||||
|             div.addClass(currentStyle).css({top: top, left: left}); | ||||
|             if (transition) { | ||||
|                 setTimeout(function() { | ||||
|                     div.css({ | ||||
|                         "transition": "none" | ||||
|                     }); | ||||
|                 },600); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         var closePopup = function(instant) { | ||||
|             $(document).off('mousedown.red-ui-popover'); | ||||
|             if (!active) { | ||||
| @@ -178,6 +350,16 @@ RED.popover = (function() { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         target.on("remove", function (ev) { | ||||
|             if (timer) { | ||||
|                 clearTimeout(timer); | ||||
|             } | ||||
|             if (active) { | ||||
|                 active = false; | ||||
|                 setTimeout(closePopup,delay.hide); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         if (trigger === 'hover') { | ||||
|             target.on('mouseenter',function(e) { | ||||
|                 clearTimeout(timer); | ||||
| @@ -236,8 +418,10 @@ RED.popover = (function() { | ||||
|             },autoClose); | ||||
|         } | ||||
|         var res = { | ||||
|             get element() { return div }, | ||||
|             setContent: function(_content) { | ||||
|                 content = _content; | ||||
|  | ||||
|                 return res; | ||||
|             }, | ||||
|             open: function (instant) { | ||||
| @@ -249,6 +433,10 @@ RED.popover = (function() { | ||||
|                 active = false; | ||||
|                 closePopup(instant); | ||||
|                 return res; | ||||
|             }, | ||||
|             move: function(options) { | ||||
|                 movePopup(options); | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|         return res; | ||||
| @@ -258,18 +446,17 @@ RED.popover = (function() { | ||||
|     return { | ||||
|         create: createPopover, | ||||
|         tooltip: function(target,content, action) { | ||||
|             var label = content; | ||||
|             if (action) { | ||||
|                 label = function() { | ||||
|                     var label = content; | ||||
|             var label = function() { | ||||
|                 var label = content; | ||||
|                 if (action) { | ||||
|                     var shortcut = RED.keyboard.getShortcut(action); | ||||
|                     if (shortcut && shortcut.key) { | ||||
|                         label = $('<span>'+content+' <span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span></span>'); | ||||
|                     } | ||||
|                     return label; | ||||
|                 } | ||||
|                 return label; | ||||
|             } | ||||
|             return RED.popover.create({ | ||||
|             var popover = RED.popover.create({ | ||||
|                 tooltip: true, | ||||
|                 target:target, | ||||
|                 trigger: "hover", | ||||
| @@ -278,6 +465,19 @@ RED.popover = (function() { | ||||
|                 content: label, | ||||
|                 delay: { show: 750, hide: 50 } | ||||
|             }); | ||||
|             popover.setContent = function(newContent) { | ||||
|                 content = newContent; | ||||
|             } | ||||
|             popover.setAction = function(newAction) { | ||||
|                 action = newAction; | ||||
|             } | ||||
|             popover.delete = function() { | ||||
|                 popover.close(true) | ||||
|                 target.off("mouseenter"); | ||||
|                 target.off("mouseleave"); | ||||
|             }; | ||||
|             return popover; | ||||
|  | ||||
|         }, | ||||
|         menu: function(options) { | ||||
|             var list = $('<ul class="red-ui-menu"></ul>'); | ||||
| @@ -286,20 +486,47 @@ RED.popover = (function() { | ||||
|             } | ||||
|             var menuOptions = options.options || []; | ||||
|             var first; | ||||
|             menuOptions.forEach(function(opt) { | ||||
|                 var item = $('<li>').appendTo(list); | ||||
|                 var link = $('<a href="#"></a>').text(opt.label).appendTo(item); | ||||
|                 link.on("click", function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     if (opt.onselect) { | ||||
|                         opt.onselect(); | ||||
|                     } | ||||
|                     menu.hide(); | ||||
|                 }) | ||||
|                 if (!first) { first = link} | ||||
|             }) | ||||
|  | ||||
|             var container = RED.popover.panel(list); | ||||
|             if (options.width) { | ||||
|                 container.container.width(options.width); | ||||
|             } | ||||
|             if (options.class) { | ||||
|                 container.container.addClass(options.class); | ||||
|             } | ||||
|             if (options.maxHeight) { | ||||
|                 container.container.css({ | ||||
|                     "max-height": options.maxHeight, | ||||
|                     "overflow-y": 'auto' | ||||
|                 }) | ||||
|             } | ||||
|             var menu = { | ||||
|                 options: function(opts) { | ||||
|                     if (opts === undefined) { | ||||
|                         return menuOptions | ||||
|                     } | ||||
|                     menuOptions = opts || []; | ||||
|                     list.empty(); | ||||
|                     menuOptions.forEach(function(opt) { | ||||
|                         var item = $('<li>').appendTo(list); | ||||
|                         var link = $('<a href="#"></a>').appendTo(item); | ||||
|                         if (typeof opt.label == "string") { | ||||
|                             link.text(opt.label) | ||||
|                         } else if (opt.label){ | ||||
|                             opt.label.appendTo(link); | ||||
|                         } | ||||
|                         link.on("click", function(evt) { | ||||
|                             evt.preventDefault(); | ||||
|                             if (opt.onselect) { | ||||
|                                 opt.onselect(); | ||||
|                             } else if (options.onselect) { | ||||
|                                 options.onselect(opt); | ||||
|                             } | ||||
|                             menu.hide(); | ||||
|                         }) | ||||
|                         if (!first) { first = link} | ||||
|                     }) | ||||
|                 }, | ||||
|                 show: function(opts) { | ||||
|                     $(document).on("keydown.red-ui-menu", function(evt) { | ||||
|                         var currentItem = list.find(":focus").parent(); | ||||
| @@ -308,11 +535,9 @@ RED.popover = (function() { | ||||
|                             // DOWN | ||||
|                             if (currentItem.length > 0) { | ||||
|                                 if (currentItem.index() === menuOptions.length-1) { | ||||
|                                     console.log("WARP TO TOP") | ||||
|                                     // Wrap to top of list | ||||
|                                     list.children().first().children().first().focus(); | ||||
|                                 } else { | ||||
|                                     console.log("GO DOWN ONE") | ||||
|                                     currentItem.next().children().first().focus(); | ||||
|                                 } | ||||
|                             } else { | ||||
| @@ -323,11 +548,9 @@ RED.popover = (function() { | ||||
|                             // UP | ||||
|                             if (currentItem.length > 0) { | ||||
|                                 if (currentItem.index() === 0) { | ||||
|                                     console.log("WARP TO BOTTOM") | ||||
|                                     // Wrap to bottom of list | ||||
|                                     list.children().last().children().first().focus(); | ||||
|                                 } else { | ||||
|                                     console.log("GO UP ONE") | ||||
|                                     currentItem.prev().children().first().focus(); | ||||
|                                 } | ||||
|                             } else { | ||||
| @@ -337,6 +560,11 @@ RED.popover = (function() { | ||||
|                             // ESCAPE | ||||
|                             evt.preventDefault(); | ||||
|                             menu.hide(true); | ||||
|                         } else if (evt.keyCode === 9 && options.tabSelect) { | ||||
|                             // TAB - with tabSelect enabled | ||||
|                             evt.preventDefault(); | ||||
|                             currentItem.find("a").trigger("click"); | ||||
|  | ||||
|                         } | ||||
|                         evt.stopPropagation(); | ||||
|                     }) | ||||
| @@ -356,6 +584,7 @@ RED.popover = (function() { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             menu.options(menuOptions); | ||||
|             return menu; | ||||
|         }, | ||||
|         panel: function(content) { | ||||
| @@ -363,7 +592,6 @@ RED.popover = (function() { | ||||
|             panel.css({ display: "none" }); | ||||
|             panel.appendTo(document.body); | ||||
|             content.appendTo(panel); | ||||
|             var closeCallback; | ||||
|  | ||||
|             function hide(dispose) { | ||||
|                 $(document).off("mousedown.red-ui-popover-panel-close"); | ||||
| @@ -378,22 +606,23 @@ RED.popover = (function() { | ||||
|             } | ||||
|             function show(options) { | ||||
|                 var closeCallback = options.onclose; | ||||
|                 var closeButton = options.closeButton; | ||||
|                 var target = options.target; | ||||
|                 var align = options.align || "right"; | ||||
|                 var offset = options.offset || [0,0]; | ||||
|  | ||||
|                 var pos = target.offset(); | ||||
|                 var targetWidth = target.width(); | ||||
|                 var targetHeight = target.height(); | ||||
|                 var targetHeight = target.outerHeight(); | ||||
|                 var panelHeight = panel.height(); | ||||
|                 var panelWidth = panel.width(); | ||||
|  | ||||
|                 var top = (targetHeight+pos.top) + offset[1]; | ||||
|                 if (top+panelHeight > $(window).height()) { | ||||
|                 if (top+panelHeight-$(document).scrollTop() > $(window).height()) { | ||||
|                     top -= (top+panelHeight)-$(window).height() + 5; | ||||
|                 } | ||||
|                 if (top < 0) { | ||||
|                     panelHeight.height(panelHeight+top) | ||||
|                     panel.height(panelHeight+top) | ||||
|                     top = 0; | ||||
|                 } | ||||
|                 if (align === "right") { | ||||
| @@ -420,7 +649,8 @@ RED.popover = (function() { | ||||
|                 }); | ||||
|  | ||||
|                 $(document).on("mousedown.red-ui-popover-panel-close", function(event) { | ||||
|                     if(!$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) { | ||||
|                     var hitCloseButton = closeButton && $(event.target).closest(closeButton).length; | ||||
|                     if(!hitCloseButton && !$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) { | ||||
|                         if (closeCallback) { | ||||
|                             closeCallback(); | ||||
|                         } | ||||
|   | ||||
| @@ -105,8 +105,8 @@ | ||||
|                     } | ||||
|                 }); | ||||
|                 this.element.on("keydown",function(e) { | ||||
|                     if (!menuShown && e.keyCode === 40) { | ||||
|                         //DOWN | ||||
|                     if (!menuShown && e.keyCode === 40 && $(this).val() === '') { | ||||
|                         //DOWN (only show menu if search field is emty) | ||||
|                         showMenu(); | ||||
|                     } | ||||
|                 }); | ||||
|   | ||||
| @@ -38,6 +38,7 @@ RED.tabs = (function() { | ||||
|         if (options.vertical) { | ||||
|             wrapper.addClass("red-ui-tabs-vertical"); | ||||
|         } | ||||
|  | ||||
|         if (options.addButton) { | ||||
|             wrapper.addClass("red-ui-tabs-add"); | ||||
|             var addButton = $('<div class="red-ui-tab-button red-ui-tabs-add"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper); | ||||
| @@ -75,6 +76,8 @@ RED.tabs = (function() { | ||||
|             }); | ||||
|         } | ||||
|         if (options.searchButton) { | ||||
|             // This is soft-deprecated as we don't use this in the core anymore | ||||
|             // We no use the `menu` option to provide a drop-down list of actions | ||||
|             wrapper.addClass("red-ui-tabs-search"); | ||||
|             var searchButton = $('<div class="red-ui-tab-button red-ui-tabs-search"><a href="#"><i class="fa fa-list-ul"></i></a></div>').appendTo(wrapper); | ||||
|             searchButton.find('a').on("click", function(evt) { | ||||
| @@ -94,17 +97,78 @@ RED.tabs = (function() { | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         if (options.menu) { | ||||
|             wrapper.addClass("red-ui-tabs-menu"); | ||||
|             var menuButton = $('<div class="red-ui-tab-button red-ui-tabs-menu"><a href="#"><i class="fa fa-caret-down"></i></a></div>').appendTo(wrapper); | ||||
|             var menuButtonLink = menuButton.find('a') | ||||
|             var menuOpen = false; | ||||
|             var menu; | ||||
|             menuButtonLink.on("click", function(evt) { | ||||
|                 evt.stopPropagation(); | ||||
|                 evt.preventDefault(); | ||||
|                 if (menuOpen) { | ||||
|                     menu.remove(); | ||||
|                     menuOpen = false; | ||||
|                     return; | ||||
|                 } | ||||
|                 menuOpen = true; | ||||
|                 var menuOptions = []; | ||||
|                 if (typeof options.searchButton === 'function') { | ||||
|                     menuOptions = options.menu() | ||||
|                 } else if (Array.isArray(options.menu)) { | ||||
|                     menuOptions = options.menu; | ||||
|                 } else if (typeof options.menu === 'function') { | ||||
|                     menuOptions = options.menu(); | ||||
|                 } | ||||
|                 menu = RED.menu.init({options: menuOptions}); | ||||
|                 menu.attr("id",options.id+"-menu"); | ||||
|                 menu.css({ | ||||
|                     position: "absolute" | ||||
|                 }) | ||||
|                 menu.appendTo("body"); | ||||
|                 var elementPos = menuButton.offset(); | ||||
|                 menu.css({ | ||||
|                     top: (elementPos.top+menuButton.height()-2)+"px", | ||||
|                     left: (elementPos.left - menu.width() + menuButton.width())+"px" | ||||
|                 }) | ||||
|                 $(".red-ui-menu.red-ui-menu-dropdown").hide(); | ||||
|                 $(document).on("click.red-ui-tabmenu", function(evt) { | ||||
|                     $(document).off("click.red-ui-tabmenu"); | ||||
|                     menuOpen = false; | ||||
|                     menu.remove(); | ||||
|                 }); | ||||
|                 menu.show(); | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         var scrollLeft; | ||||
|         var scrollRight; | ||||
|  | ||||
|         if (options.scrollable) { | ||||
|             wrapper.addClass("red-ui-tabs-scrollable"); | ||||
|             scrollContainer.addClass("red-ui-tabs-scroll-container"); | ||||
|             scrollContainer.on("scroll",updateScroll); | ||||
|             scrollContainer.on("scroll",function(evt) { | ||||
|                 // Generated by trackpads - not mousewheel | ||||
|                 updateScroll(evt); | ||||
|             }); | ||||
|             scrollContainer.on("wheel", function(evt) { | ||||
|                 if (evt.originalEvent.deltaX === 0) { | ||||
|                     // Prevent the scroll event from firing | ||||
|                     evt.preventDefault(); | ||||
|  | ||||
|                     // Assume this is wheel event which might not trigger | ||||
|                     // the scroll event, so do things manually | ||||
|                     var sl = scrollContainer.scrollLeft(); | ||||
|                     sl -= evt.originalEvent.deltaY; | ||||
|                     scrollContainer.scrollLeft(sl); | ||||
|                 } | ||||
|             }) | ||||
|             scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a"); | ||||
|             scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();}); | ||||
|             scrollLeft.on('mousedown',function(evt) {scrollEventHandler(evt, evt.shiftKey?('-='+scrollContainer.scrollLeft()):'-=150') }).on('click',function(evt){ evt.preventDefault();}); | ||||
|             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();}); | ||||
|             scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,evt.shiftKey?('+='+(scrollContainer[0].scrollWidth - scrollContainer.width()-scrollContainer.scrollLeft())):'+=150') }).on('click',function(evt){ evt.preventDefault();}); | ||||
|         } | ||||
|  | ||||
|         if (options.collapsible) { | ||||
| @@ -322,6 +386,12 @@ RED.tabs = (function() { | ||||
|             if (link.length === 0) { | ||||
|                 return; | ||||
|             } | ||||
|             if (link.parent().hasClass("hide-tab")) { | ||||
|                 link.parent().removeClass("hide-tab").removeClass("hide"); | ||||
|                 if (options.onshow) { | ||||
|                     options.onshow(tabs[link.attr('href').slice(1)]) | ||||
|                 } | ||||
|             } | ||||
|             if (!link.parent().hasClass("active")) { | ||||
|                 ul.children().removeClass("active"); | ||||
|                 ul.children().css({"transition": "width 100ms"}); | ||||
| @@ -347,13 +417,13 @@ RED.tabs = (function() { | ||||
|             } | ||||
|         } | ||||
|         function activatePreviousTab() { | ||||
|             var previous = ul.find("li.active").prev(); | ||||
|             var previous = findPreviousVisibleTab(); | ||||
|             if (previous.length > 0) { | ||||
|                 activateTab(previous.find("a")); | ||||
|             } | ||||
|         } | ||||
|         function activateNextTab() { | ||||
|             var next = ul.find("li.active").next(); | ||||
|             var next = findNextVisibleTab(); | ||||
|             if (next.length > 0) { | ||||
|                 activateTab(next.find("a")); | ||||
|             } | ||||
| @@ -363,7 +433,9 @@ RED.tabs = (function() { | ||||
|             if (options.vertical) { | ||||
|                 return; | ||||
|             } | ||||
|             var tabs = ul.find("li.red-ui-tab"); | ||||
|             var allTabs = ul.find("li.red-ui-tab"); | ||||
|             var tabs = allTabs.filter(":not(.hide-tab)"); | ||||
|             var hiddenTabs = allTabs.filter(".hide-tab"); | ||||
|             var width = wrapper.width(); | ||||
|             var tabCount = tabs.length; | ||||
|             var tabWidth; | ||||
| @@ -373,7 +445,7 @@ RED.tabs = (function() { | ||||
|                 var visibleCount = collapsedButtonsRow.children(":visible").length; | ||||
|                 tabWidth = width - collapsedButtonsRow.width()-10; | ||||
|                 var maxTabWidth = 198; | ||||
|                 var minTabWidth = 80; | ||||
|                 var minTabWidth = 120; | ||||
|                 if (tabWidth <= minTabWidth || (tabWidth < maxTabWidth && visibleCount > 5)) { | ||||
|                     // The tab is too small. Hide the next button to make room | ||||
|                     // Start at the end of the button row, -1 for the menu button | ||||
| @@ -431,6 +503,7 @@ RED.tabs = (function() { | ||||
|                 // } | ||||
|  | ||||
|                 tabs.css({width:currentTabWidth}); | ||||
|                 hiddenTabs.css({width:"0px"}); | ||||
|                 if (tabWidth < 50) { | ||||
|                     // ul.find(".red-ui-tab-close").hide(); | ||||
|                     ul.find(".red-ui-tab-icon").hide(); | ||||
| @@ -471,12 +544,19 @@ RED.tabs = (function() { | ||||
|             } | ||||
|             var li = ul.find("a[href='#"+id+"']").parent(); | ||||
|             if (li.hasClass("active")) { | ||||
|                 var tab = li.prev(); | ||||
|                 var tab = findPreviousVisibleTab(li); | ||||
|                 if (tab.length === 0) { | ||||
|                     tab = li.next(); | ||||
|                     tab = findNextVisibleTab(li); | ||||
|                 } | ||||
|                 if (tab.length > 0) { | ||||
|                     activateTab(tab.find("a")); | ||||
|                 } else { | ||||
|                     if (options.onchange) { | ||||
|                         options.onchange(null); | ||||
|                     } | ||||
|                 } | ||||
|                 activateTab(tab.find("a")); | ||||
|             } | ||||
|  | ||||
|             li.remove(); | ||||
|             if (tabs[id].pinned) { | ||||
|                 pinnedTabsCount--; | ||||
| @@ -492,6 +572,75 @@ RED.tabs = (function() { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function findPreviousVisibleTab(li) { | ||||
|             if (!li) { | ||||
|                 li = ul.find("li.active"); | ||||
|             } | ||||
|             var previous = li.prev(); | ||||
|             while(previous.length > 0 && previous.hasClass("hide-tab")) { | ||||
|                 previous = previous.prev(); | ||||
|             } | ||||
|             return previous; | ||||
|         } | ||||
|         function findNextVisibleTab(li) { | ||||
|             if (!li) { | ||||
|                 li = ul.find("li.active"); | ||||
|             } | ||||
|             var next = li.next(); | ||||
|             while(next.length > 0 && next.hasClass("hide-tab")) { | ||||
|                 next = next.next(); | ||||
|             } | ||||
|             return next; | ||||
|         } | ||||
|         function showTab(id) { | ||||
|             if (tabs[id]) { | ||||
|                 var li = ul.find("a[href='#"+id+"']").parent(); | ||||
|                 if (li.hasClass("hide-tab")) { | ||||
|                     li.removeClass("hide-tab").removeClass("hide"); | ||||
|                     if (ul.find("li.red-ui-tab:not(.hide-tab)").length === 1) { | ||||
|                         activateTab(li.find("a")) | ||||
|                     } | ||||
|                     updateTabWidths(); | ||||
|                     if (options.onshow) { | ||||
|                         options.onshow(tabs[id]) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         function hideTab(id) { | ||||
|             if (tabs[id]) { | ||||
|                 var li = ul.find("a[href='#"+id+"']").parent(); | ||||
|                 if (!li.hasClass("hide-tab")) { | ||||
|                     if (li.hasClass("active")) { | ||||
|                         var tab = findPreviousVisibleTab(li); | ||||
|                         if (tab.length === 0) { | ||||
|                             tab = findNextVisibleTab(li); | ||||
|                         } | ||||
|                         if (tab.length > 0) { | ||||
|                             activateTab(tab.find("a")); | ||||
|                         } else { | ||||
|                             if (options.onchange) { | ||||
|                                 options.onchange(null); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     li.removeClass("active"); | ||||
|                     li.one("transitionend", function(evt) { | ||||
|                         li.addClass("hide"); | ||||
|                         updateTabWidths(); | ||||
|                         if (options.onhide) { | ||||
|                             options.onhide(tabs[id]) | ||||
|                         } | ||||
|                         setTimeout(function() { | ||||
|                             updateScroll() | ||||
|                         },200) | ||||
|                     }) | ||||
|                     li.addClass("hide-tab"); | ||||
|                     li.css({width:0}) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var tabAPI =  { | ||||
|             addTab: function(tab,targetIndex) { | ||||
|                 if (options.onselect) { | ||||
| @@ -521,7 +670,7 @@ RED.tabs = (function() { | ||||
|                 } | ||||
|                 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); | ||||
|                     $('<i>',{class:"red-ui-tab-icon", style:"mask-image: url("+tab.icon+"); -webkit-mask-image: url("+tab.icon+");"}).appendTo(link); | ||||
|                 } else if (tab.iconClass) { | ||||
|                     $('<i>',{class:"red-ui-tab-icon "+tab.iconClass}).appendTo(link); | ||||
|                 } | ||||
| @@ -648,6 +797,7 @@ RED.tabs = (function() { | ||||
|                 link.on("click", function(evt) { evt.preventDefault(); }) | ||||
|                 link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); }) | ||||
|  | ||||
|                 $('<span class="red-ui-tabs-fade"></span>').appendTo(li); | ||||
|  | ||||
|                 if (tab.closeable) { | ||||
|                     li.addClass("red-ui-tabs-closeable") | ||||
| @@ -657,6 +807,18 @@ RED.tabs = (function() { | ||||
|                         event.preventDefault(); | ||||
|                         removeTab(tab.id); | ||||
|                     }); | ||||
|                     RED.popover.tooltip(closeLink,RED._("workspace.hideFlow")); | ||||
|                 } | ||||
|                 if (tab.hideable) { | ||||
|                     li.addClass("red-ui-tabs-closeable") | ||||
|                     var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li); | ||||
|                     closeLink.append('<i class="fa fa-eye" />'); | ||||
|                     closeLink.append('<i class="fa fa-eye-slash" />'); | ||||
|                     closeLink.on("click",function(event) { | ||||
|                         event.preventDefault(); | ||||
|                         hideTab(tab.id); | ||||
|                     }); | ||||
|                     RED.popover.tooltip(closeLink,RED._("workspace.hideFlow")); | ||||
|                 } | ||||
|  | ||||
|                 var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li); | ||||
| @@ -664,10 +826,13 @@ RED.tabs = (function() { | ||||
|                     $('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges); | ||||
|                     $('<i class="red-ui-tabs-badge-selected fa fa-check-circle"></i>').appendTo(badges); | ||||
|                 } | ||||
|  | ||||
|                 // link.attr("title",tab.label); | ||||
|                 RED.popover.tooltip(link,function() { return tab.label}) | ||||
|  | ||||
|                 if (options.onadd) { | ||||
|                     options.onadd(tab); | ||||
|                 } | ||||
|                 link.attr("title",tab.label); | ||||
|                 if (ul.find("li.red-ui-tab").length == 1) { | ||||
|                     activateTab(link); | ||||
|                 } | ||||
| @@ -768,19 +933,37 @@ RED.tabs = (function() { | ||||
|             previousTab: activatePreviousTab, | ||||
|             resize: updateTabWidths, | ||||
|             count: function() { | ||||
|                 return ul.find("li.red-ui-tab").length; | ||||
|                 return ul.find("li.red-ui-tab:not(.hide)").length; | ||||
|             }, | ||||
|             activeIndex: function() { | ||||
|                 return ul.find("li.active").index() | ||||
|             }, | ||||
|             contains: function(id) { | ||||
|                 return ul.find("a[href='#"+id+"']").length > 0; | ||||
|             }, | ||||
|             showTab: showTab, | ||||
|             hideTab: hideTab, | ||||
|  | ||||
|             renameTab: function(id,label) { | ||||
|                 tabs[id].label = label; | ||||
|                 var tab = ul.find("a[href='#"+id+"']"); | ||||
|                 tab.attr("title",label); | ||||
|                 tab.find("span.red-ui-text-bidi-aware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label)); | ||||
|                 updateTabWidths(); | ||||
|             }, | ||||
|             listTabs: function() { | ||||
|                 return $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); | ||||
|             }, | ||||
|             selection: getSelection, | ||||
|             clearSelection: function() { | ||||
|                 if (options.onselect) { | ||||
|                     var selection = ul.find("li.red-ui-tab.selected"); | ||||
|                     if (selection.length > 0) { | ||||
|                         selection.removeClass("selected"); | ||||
|                         selectionChanged(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             }, | ||||
|             order: function(order) { | ||||
|                 preferredOrder = order; | ||||
|                 var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); | ||||
|   | ||||
| @@ -48,7 +48,7 @@ | ||||
|             }); | ||||
|             this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"></button>'); | ||||
|             if (enabledLabel || disabledLabel) { | ||||
|                 this.buttonLabel = $("<span>").appendTo(this.button); | ||||
|                 this.buttonLabel = $("<span>").appendTo(this.button).css("margin-left", "5px"); | ||||
|             } | ||||
|  | ||||
|             if (this.options.class) { | ||||
|   | ||||
| @@ -24,6 +24,9 @@ | ||||
|  *   - rootSortable: boolean - if 'sortable' is set, then setting this to | ||||
|  *                             false, prevents items being sorted to the | ||||
|  *                             top level of the tree | ||||
|  *   - autoSelect: boolean - default true - triggers item selection when navigating | ||||
|  *                           list by keyboard. If the list has checkboxed items | ||||
|  *                           you probably want to set this to false | ||||
|  * | ||||
|  * methods: | ||||
|  *   - data(items) - clears existing items and replaces with new data | ||||
| @@ -41,6 +44,7 @@ | ||||
|  *         sublabel: 'Local', // a sub-label for the item | ||||
|  *         icon: 'fa fa-rocket', // (optional) icon for the item | ||||
|  *         checkbox: true/false, // (optional) if present, display checkbox accordingly | ||||
|  *         radio: 'group-name',  // (optional) if present, display radio box - using group-name to set radio group | ||||
|  *         selected: true/false, // (optional) whether the item is selected or not | ||||
|  *         children: [] | function(done,item) // (optional) an array of child items, or a function | ||||
|  *                                       // that will call the `done` callback with an array | ||||
| @@ -49,6 +53,7 @@ | ||||
|  *         deferBuild: true/false, // don't build any ui elements for the item's children | ||||
|  *                                    until it is expanded by the user. | ||||
|  *         element: // custom dom element to use for the item - ignored if `label` is set | ||||
|  *         collapsible: true/false, // prevent a parent item from being collapsed. default true. | ||||
|  *     } | ||||
|  * ] | ||||
|  * | ||||
| @@ -89,77 +94,99 @@ | ||||
|     $.widget( "nodered.treeList", { | ||||
|         _create: function() { | ||||
|             var that = this; | ||||
|  | ||||
|             var autoSelect = true; | ||||
|             if (that.options.autoSelect === false) { | ||||
|                 autoSelect = false; | ||||
|             } | ||||
|             this.element.addClass('red-ui-treeList'); | ||||
|             this.element.attr("tabIndex",0); | ||||
|             var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element); | ||||
|             this.element.on('keydown', function(evt) { | ||||
|                 var selected = that._topList.find(".selected").parent().data('data'); | ||||
|                 if (!selected && (evt.keyCode === 40 || evt.keyCode === 38)) { | ||||
|                     that.select(that._data[0]); | ||||
|                 var focussed = that._topList.find(".focus").parent().data('data'); | ||||
|                 if (!focussed && (evt.keyCode === 40 || evt.keyCode === 38)) { | ||||
|                     if (that._data[0]) { | ||||
|                         if (autoSelect) { | ||||
|                             that.select(that._data[0]); | ||||
|                         } else { | ||||
|                             that._topList.find(".focus").removeClass("focus") | ||||
|                         } | ||||
|                         that._data[0].treeList.label.addClass('focus') | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|                 var target; | ||||
|                 switch(evt.keyCode) { | ||||
|                     case 32: // SPACE | ||||
|                     case 13: // ENTER | ||||
|                         if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) { | ||||
|                             return | ||||
|                         } | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children) { | ||||
|                             if (selected.treeList.container.hasClass("expanded")) { | ||||
|                                 selected.treeList.collapse() | ||||
|                         if (focussed.checkbox) { | ||||
|                             focussed.treeList.checkbox.trigger("click"); | ||||
|                         } else if (focussed.radio) { | ||||
|                             focussed.treeList.radio.trigger("click"); | ||||
|                         } else if (focussed.children) { | ||||
|                             if (focussed.treeList.container.hasClass("expanded")) { | ||||
|                                 focussed.treeList.collapse() | ||||
|                             } else { | ||||
|                                 selected.treeList.expand() | ||||
|                                 focussed.treeList.expand() | ||||
|                             } | ||||
|                         } else { | ||||
|                             that._trigger("confirm",null,selected) | ||||
|                             that._trigger("confirm",null,focussed) | ||||
|                         } | ||||
|  | ||||
|                     break; | ||||
|                     case 37: // LEFT | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children&& selected.treeList.container.hasClass("expanded")) { | ||||
|                             selected.treeList.collapse() | ||||
|                         } else if (selected.parent) { | ||||
|                             target = selected.parent; | ||||
|                         if (focussed.children&& focussed.treeList.container.hasClass("expanded")) { | ||||
|                             focussed.treeList.collapse() | ||||
|                         } else if (focussed.parent) { | ||||
|                             target = focussed.parent; | ||||
|                         } | ||||
|                     break; | ||||
|                     case 38: // UP | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         target = that._getPreviousSibling(selected); | ||||
|                         target = that._getPreviousSibling(focussed); | ||||
|                         if (target) { | ||||
|                             target = that._getLastDescendant(target); | ||||
|                         } | ||||
|                         if (!target && selected.parent) { | ||||
|                             target = selected.parent; | ||||
|                         if (!target && focussed.parent) { | ||||
|                             target = focussed.parent; | ||||
|                         } | ||||
|                     break; | ||||
|                     case 39: // RIGHT | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children) { | ||||
|                             if (!selected.treeList.container.hasClass("expanded")) { | ||||
|                                 selected.treeList.expand() | ||||
|                         if (focussed.children) { | ||||
|                             if (!focussed.treeList.container.hasClass("expanded")) { | ||||
|                                 focussed.treeList.expand() | ||||
|                             } | ||||
|                         } | ||||
|                     break | ||||
|                     case 40: //DOWN | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) { | ||||
|                             target = selected.children[0]; | ||||
|                         if (focussed.children && Array.isArray(focussed.children) && focussed.children.length > 0 && focussed.treeList.container.hasClass("expanded")) { | ||||
|                             target = focussed.children[0]; | ||||
|                         } else { | ||||
|                             target = that._getNextSibling(selected); | ||||
|                             while (!target && selected.parent) { | ||||
|                                 selected = selected.parent; | ||||
|                                 target = that._getNextSibling(selected); | ||||
|                             target = that._getNextSibling(focussed); | ||||
|                             while (!target && focussed.parent) { | ||||
|                                 focussed = focussed.parent; | ||||
|                                 target = that._getNextSibling(focussed); | ||||
|                             } | ||||
|                         } | ||||
|                     break | ||||
|                 } | ||||
|                 if (target) { | ||||
|                     that.select(target); | ||||
|                     if (autoSelect) { | ||||
|                         that.select(target); | ||||
|                     } else { | ||||
|                         that._topList.find(".focus").removeClass("focus") | ||||
|                     } | ||||
|                     target.treeList.label.addClass('focus') | ||||
|                 } | ||||
|             }); | ||||
|             this._data = []; | ||||
| @@ -312,7 +339,8 @@ | ||||
|             } | ||||
|             if (child.depth !== parent.depth+1) { | ||||
|                 child.depth = parent.depth+1; | ||||
|                 var labelPaddingWidth = ((child.gutter ? child.gutter[0].offsetWidth + 2 : 0) + (child.depth * 20)); | ||||
|                 // var labelPaddingWidth = ((child.gutter ? child.gutter[0].offsetWidth + 2 : 0) + (child.depth * 20)); | ||||
|                 var labelPaddingWidth = (((child.gutter&&!child.gutter.hasClass("red-ui-treeList-gutter-float"))?child.gutter.width()+2:0)+(child.depth*20)); | ||||
|                 child.treeList.labelPadding.width(labelPaddingWidth+'px'); | ||||
|                 if (child.element) { | ||||
|                     $(child.element).css({ | ||||
| @@ -350,10 +378,12 @@ | ||||
|                 delete that._items[item.id]; | ||||
|                 if(item.depth === 0) { | ||||
|                     for(var key in that._items) { | ||||
|                         var child = that._items[key]; | ||||
|                         if(child.parent && child.parent.id === item.id) { | ||||
|                             delete that._items[key].treeList; | ||||
|                             delete that._items[key]; | ||||
|                         if (that._items.hasOwnProperty(key)) { | ||||
|                             var child = that._items[key]; | ||||
|                             if(child.parent && child.parent.id === item.id) { | ||||
|                                 delete that._items[key].treeList; | ||||
|                                 delete that._items[key]; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     that._data = that._data.filter(function(data) { return data.id !== item.id}) | ||||
| @@ -459,6 +489,9 @@ | ||||
|                 container.addClass("expanded"); | ||||
|             } | ||||
|             item.treeList.collapse = function() { | ||||
|                 if (item.collapsible === false) { | ||||
|                     return | ||||
|                 } | ||||
|                 if (!item.children) { | ||||
|                     return; | ||||
|                 } | ||||
| @@ -529,9 +562,12 @@ | ||||
|                 }).appendTo(label) | ||||
|  | ||||
|             } | ||||
|             var labelPaddingWidth = (item.gutter ? item.gutter[0].offsetWidth + 2 : 0) + (depth * 20) | ||||
|  | ||||
|             var labelPaddingWidth = ((item.gutter&&!item.gutter.hasClass("red-ui-treeList-gutter-float"))?item.gutter.width()+2:0)+(depth*20); | ||||
|  | ||||
|             item.treeList.labelPadding = $('<span>').css({ | ||||
|                 display: "inline-block", | ||||
|                 "flex-shrink": 0, | ||||
|                 width:  labelPaddingWidth+'px' | ||||
|             }).appendTo(label); | ||||
|  | ||||
| @@ -577,7 +613,7 @@ | ||||
|                     // Already a parent because we've got the angle-right icon | ||||
|                     return; | ||||
|                 } | ||||
|                 $('<i class="fa fa-angle-right" />').appendTo(treeListIcon); | ||||
|                 $('<i class="fa fa-angle-right" />').toggleClass("hide",item.collapsible === false).appendTo(treeListIcon); | ||||
|                 treeListIcon.on("click.red-ui-treeList-expand", function(e) { | ||||
|                         e.stopPropagation(); | ||||
|                         e.preventDefault(); | ||||
| @@ -628,6 +664,46 @@ | ||||
|                     label.on("click", function(e) { | ||||
|                         e.stopPropagation(); | ||||
|                         cb.trigger("click"); | ||||
|                         that._topList.find(".focus").removeClass("focus") | ||||
|                         label.addClass('focus') | ||||
|                     }) | ||||
|                 } | ||||
|                 item.treeList.select = function(v) { | ||||
|                     if (v !== item.selected) { | ||||
|                         cb.trigger("click"); | ||||
|                     } | ||||
|                 } | ||||
|                 item.treeList.checkbox = cb; | ||||
|                 selectWrapper.appendTo(label) | ||||
|             } else if (item.radio) { | ||||
|                 var selectWrapper = $('<span class="red-ui-treeList-icon"></span>'); | ||||
|                 var cb = $('<input class="red-ui-treeList-radio" type="radio">').prop('name', item.radio).prop('checked',item.selected).appendTo(selectWrapper); | ||||
|                 cb.on('click', function(e) { | ||||
|                     e.stopPropagation(); | ||||
|                 }); | ||||
|                 cb.on('change', function(e) { | ||||
|                     item.selected = this.checked; | ||||
|                     that._selected.forEach(function(selectedItem) { | ||||
|                         if (selectedItem.radio === item.radio) { | ||||
|                             selectedItem.treeList.label.removeClass("selected"); | ||||
|                             selectedItem.selected = false; | ||||
|                             that._selected.delete(selectedItem); | ||||
|                         } | ||||
|                     }) | ||||
|                     if (item.selected) { | ||||
|                         that._selected.add(item); | ||||
|                     } else { | ||||
|                         that._selected.delete(item); | ||||
|                     } | ||||
|                     label.toggleClass("selected",this.checked); | ||||
|                     that._trigger("select",e,item); | ||||
|                 }) | ||||
|                 if (!item.children) { | ||||
|                     label.on("click", function(e) { | ||||
|                         e.stopPropagation(); | ||||
|                         cb.trigger("click"); | ||||
|                         that._topList.find(".focus").removeClass("focus") | ||||
|                         label.addClass('focus') | ||||
|                     }) | ||||
|                 } | ||||
|                 item.treeList.select = function(v) { | ||||
| @@ -636,6 +712,7 @@ | ||||
|                     } | ||||
|                 } | ||||
|                 selectWrapper.appendTo(label) | ||||
|                 item.treeList.radio = cb; | ||||
|             } else { | ||||
|                 label.on("click", function(e) { | ||||
|                     if (!that.options.multi) { | ||||
| @@ -643,10 +720,14 @@ | ||||
|                     } | ||||
|                     label.addClass("selected"); | ||||
|                     that._selected.add(item); | ||||
|                     that._topList.find(".focus").removeClass("focus") | ||||
|                     label.addClass('focus') | ||||
|  | ||||
|                     that._trigger("select",e,item) | ||||
|                 }) | ||||
|                 label.on("dblclick", function(e) { | ||||
|                     that._topList.find(".focus").removeClass("focus") | ||||
|                     label.addClass('focus') | ||||
|                     if (!item.children) { | ||||
|                         that._trigger("confirm",e,item); | ||||
|                     } | ||||
| @@ -794,6 +875,9 @@ | ||||
|             if (item.treeList.label) { | ||||
|                 item.treeList.label.addClass("selected"); | ||||
|             } | ||||
|  | ||||
|             that._topList.find(".focus").removeClass("focus"); | ||||
|  | ||||
|             if (triggerEvent !== false) { | ||||
|                 this._trigger("select",null,item) | ||||
|             } | ||||
| @@ -801,6 +885,9 @@ | ||||
|         clearSelection: function() { | ||||
|             this._selected.forEach(function(item) { | ||||
|                 item.selected = false; | ||||
|                 if (item.treeList.checkbox) { | ||||
|                     item.treeList.checkbox.prop('checked',false) | ||||
|                 } | ||||
|                 if (item.treeList.label) { | ||||
|                     item.treeList.label.removeClass("selected") | ||||
|                 } | ||||
|   | ||||
| @@ -53,8 +53,120 @@ | ||||
|         } | ||||
|         return icon; | ||||
|     } | ||||
|  | ||||
|     var autoComplete = function(options) { | ||||
|         function getMatch(value, searchValue) { | ||||
|             const idx = value.toLowerCase().indexOf(searchValue.toLowerCase()); | ||||
|             const len = idx > -1 ? searchValue.length : 0; | ||||
|             return { | ||||
|                 index: idx, | ||||
|                 found: idx > -1, | ||||
|                 pre: value.substring(0,idx), | ||||
|                 match: value.substring(idx,idx+len), | ||||
|                 post: value.substring(idx+len), | ||||
|             } | ||||
|         } | ||||
|         function generateSpans(match) { | ||||
|             const els = []; | ||||
|             if(match.pre) { els.push($('<span/>').text(match.pre)); } | ||||
|             if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); } | ||||
|             if(match.post) { els.push($('<span/>').text(match.post)); } | ||||
|             return els; | ||||
|         } | ||||
|         return function(val) { | ||||
|             var matches = []; | ||||
|             options.forEach(opt => { | ||||
|                 const optVal = opt.value; | ||||
|                 const optSrc = (opt.source||[]).join(","); | ||||
|                 const valMatch = getMatch(optVal, val); | ||||
|                 const srcMatch = getMatch(optSrc, val); | ||||
|                 if (valMatch.found || srcMatch.found) { | ||||
|                     const element = $('<div>',{style: "display: flex"}); | ||||
|                     const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); | ||||
|                     valEl.append(generateSpans(valMatch)); | ||||
|                     valEl.appendTo(element); | ||||
|                     if (optSrc) { | ||||
|                         const optEl = $('<div>').css({ "font-size": "0.8em" }); | ||||
|                         optEl.append(generateSpans(srcMatch)); | ||||
|                         optEl.appendTo(element); | ||||
|                     } | ||||
|                     matches.push({  | ||||
|                         value: optVal,  | ||||
|                         label: element,  | ||||
|                         i: (valMatch.found ? valMatch.index : srcMatch.index)  | ||||
|                     }); | ||||
|                 } | ||||
|             }) | ||||
|             matches.sort(function(A,B){return A.i-B.i}) | ||||
|             return matches; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // This is a hand-generated list of completions for the core nodes (based on the node help html). | ||||
|     var msgCompletions = [ | ||||
|         { value: "payload" }, | ||||
|         { value: "topic", source: ["mqtt","inject","rbe"] }, | ||||
|         { value: "action", source: ["mqtt"] }, | ||||
|         { value: "complete", source: ["join"] }, | ||||
|         { value: "contentType", source: ["mqtt"] }, | ||||
|         { value: "cookies", source: ["http request","http response"] }, | ||||
|         { value: "correlationData", source: ["mqtt"] }, | ||||
|         { value: "delay", source: ["delay","trigger"] }, | ||||
|         { value: "encoding", source: ["file"] }, | ||||
|         { value: "error", source: ["catch"] }, | ||||
|         { value: "error.message", source: ["catch"] }, | ||||
|         { value: "error.source", source: ["catch"] }, | ||||
|         { value: "error.source.id", source: ["catch"] }, | ||||
|         { value: "error.source.type", source: ["catch"] }, | ||||
|         { value: "error.source.name", source: ["catch"] }, | ||||
|         { value: "filename", source: ["file","file in"] }, | ||||
|         { value: "flush", source: ["delay"] }, | ||||
|         { value: "followRedirects", source: ["http request"] }, | ||||
|         { value: "headers", source: ["http response","http request"] }, | ||||
|         { value: "host", source: ["tcp request","http request"] }, | ||||
|         { value: "ip", source: ["udp out"] }, | ||||
|         { value: "kill", source: ["exec"] }, | ||||
|         { value: "messageExpiryInterval", source: ["mqtt"] }, | ||||
|         { value: "method", source: ["http request"] }, | ||||
|         { value: "options", source: ["xml"] }, | ||||
|         { value: "parts", source: ["split","join","batch","sort"] }, | ||||
|         { value: "pid", source: ["exec"] }, | ||||
|         { value: "port", source: ["tcp request"," udp out"] }, | ||||
|         { value: "qos", source: ["mqtt"] }, | ||||
|         { value: "rate", source: ["delay"] }, | ||||
|         { value: "rejectUnauthorized", source: ["http request"] }, | ||||
|         { value: "req", source: ["http in"]}, | ||||
|         { value: "req.body", source: ["http in"]}, | ||||
|         { value: "req.headers", source: ["http in"]}, | ||||
|         { value: "req.query", source: ["http in"]}, | ||||
|         { value: "req.params", source: ["http in"]}, | ||||
|         { value: "req.cookies", source: ["http in"]}, | ||||
|         { value: "req.files", source: ["http in"]}, | ||||
|         { value: "requestTimeout", source: ["http request"] }, | ||||
|         { value: "reset", source: ["delay","trigger","join","rbe"] }, | ||||
|         { value: "responseCookies", source: ["http request"] }, | ||||
|         { value: "responseTopic", source: ["mqtt"] }, | ||||
|         { value: "responseURL", source: ["http request"] }, | ||||
|         { value: "restartTimeout", source: ["join"] }, | ||||
|         { value: "retain", source: ["mqtt"] }, | ||||
|         { value: "schema", source: ["json"] }, | ||||
|         { value: "select", source: ["html"] }, | ||||
|         { value: "statusCode", source: ["http response","http request"] }, | ||||
|         { value: "status", source: ["status"] }, | ||||
|         { value: "status.text", source: ["status"] }, | ||||
|         { value: "status.source", source: ["status"] }, | ||||
|         { value: "status.source.type", source: ["status"] }, | ||||
|         { value: "status.source.id", source: ["status"] }, | ||||
|         { value: "status.source.name", source: ["status"] }, | ||||
|         { value: "target", source: ["link call"] }, | ||||
|         { value: "template", source: ["template"] }, | ||||
|         { value: "toFront", source: ["delay"] }, | ||||
|         { value: "url", source: ["http request"] }, | ||||
|         { value: "userProperties", source: ["mqtt"] }, | ||||
|         { value: "_session", source: ["websocket out","tcp out"] }, | ||||
|     ] | ||||
|     var allOptions = { | ||||
|         msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression}, | ||||
|         msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)}, | ||||
|         flow: {value:"flow",label:"flow.",hasValue:true, | ||||
|             options:[], | ||||
|             validate:RED.utils.validatePropertyExpression, | ||||
| @@ -86,6 +198,8 @@ | ||||
|                 } | ||||
|                 RED.editor.editJSON({ | ||||
|                     value: value, | ||||
|                     stateId: RED.editor.generateViewStateId("typedInput", that, "json"), | ||||
|                     focus: true, | ||||
|                     complete: function(v) { | ||||
|                         var value = v; | ||||
|                         try { | ||||
| @@ -108,6 +222,8 @@ | ||||
|                 var that = this; | ||||
|                 RED.editor.editExpression({ | ||||
|                     value: this.value().replace(/\t/g,"\n"), | ||||
|                     stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"), | ||||
|                     focus: true, | ||||
|                     complete: function(v) { | ||||
|                         that.value(v.replace(/\n/g,"\t")); | ||||
|                     } | ||||
| @@ -122,6 +238,8 @@ | ||||
|                 var that = this; | ||||
|                 RED.editor.editBuffer({ | ||||
|                     value: this.value(), | ||||
|                     stateId: RED.editor.generateViewStateId("typedInput", that, "bin"), | ||||
|                     focus: true, | ||||
|                     complete: function(v) { | ||||
|                         that.value(v); | ||||
|                     } | ||||
| @@ -265,6 +383,47 @@ | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     // For a type with options, check value is a valid selection | ||||
|     // If !opt.multiple, returns the valid option object | ||||
|     // if opt.multiple, returns an array of valid option objects | ||||
|     // If not valid, returns null; | ||||
|  | ||||
|     function isOptionValueValid(opt, currentVal) { | ||||
|         if (!opt.multiple) { | ||||
|             for (var i=0;i<opt.options.length;i++) { | ||||
|                 op = opt.options[i]; | ||||
|                 if (typeof op === "string" && op === currentVal) { | ||||
|                     return {value:currentVal} | ||||
|                 } else if (op.value === currentVal) { | ||||
|                     return op; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // Check to see if value is a valid csv of | ||||
|             // options. | ||||
|             var currentValues = {}; | ||||
|             var selected = []; | ||||
|             currentVal.split(",").forEach(function(v) { | ||||
|                 if (v) { | ||||
|                     currentValues[v] = true; | ||||
|                 } | ||||
|             }); | ||||
|             for (var i=0;i<opt.options.length;i++) { | ||||
|                 op = opt.options[i]; | ||||
|                 var val = typeof op === "string" ? op : op.value; | ||||
|                 if (currentValues.hasOwnProperty(val)) { | ||||
|                     delete currentValues[val]; | ||||
|                     selected.push(typeof op === "string" ? {value:op} : op.value) | ||||
|                 } | ||||
|             } | ||||
|             if (!$.isEmptyObject(currentValues)) { | ||||
|                 return null; | ||||
|             } | ||||
|             return selected | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     var nlsd = false; | ||||
|  | ||||
|     $.widget( "nodered.typedInput", { | ||||
| @@ -298,7 +457,8 @@ | ||||
|             } | ||||
|             nlsd = true; | ||||
|             var that = this; | ||||
|  | ||||
|             this.identifier = this.element.attr('id') || "TypedInput-"+Math.floor(Math.random()*100); | ||||
|             if (this.options.debug) { console.log(this.identifier,"Create",{defaultType:this.options.default, value:this.element.val()}) } | ||||
|             this.disarmClick = false; | ||||
|             this.input = $('<input class="red-ui-typedInput-input" type="text"></input>'); | ||||
|             this.input.insertAfter(this.element); | ||||
| @@ -328,6 +488,8 @@ | ||||
|             }); | ||||
|  | ||||
|             this.defaultInputType = this.input.attr('type'); | ||||
|             // Used to remember selections per-type to restore them when switching between types | ||||
|             this.oldValues = {}; | ||||
|  | ||||
|             this.uiSelect.addClass("red-ui-typedInput-container"); | ||||
|  | ||||
| @@ -380,6 +542,9 @@ | ||||
|                 that.element.trigger('paste',evt); | ||||
|             }); | ||||
|             this.input.on('keydown', function(evt) { | ||||
|                 if (that.typeMap[that.propertyType].autoComplete) { | ||||
|                     return | ||||
|                 } | ||||
|                 if (evt.keyCode >= 37 && evt.keyCode <= 40) { | ||||
|                     evt.stopPropagation(); | ||||
|                 } | ||||
| @@ -407,9 +572,9 @@ | ||||
|             // 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="red-ui-typedInput-icon fa fa-caret-down"></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; | ||||
|             }); | ||||
|             // RED.popover.tooltip(this.optionSelectLabel,function() { | ||||
|             //     return that.optionValue; | ||||
|             // }); | ||||
|             this.optionSelectTrigger.on("click", function(event) { | ||||
|                 event.preventDefault(); | ||||
|                 event.stopPropagation(); | ||||
| @@ -428,7 +593,9 @@ | ||||
|  | ||||
|             this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect); | ||||
|             this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton); | ||||
|             this.type(this.options.default||this.typeList[0].value); | ||||
|  | ||||
|             this.type(this.typeField.val() || this.options.default||this.typeList[0].value); | ||||
|             this.typeChanged = !!this.options.default; | ||||
|         }catch(err) { | ||||
|             console.log(err.stack); | ||||
|         } | ||||
| @@ -508,7 +675,7 @@ | ||||
|                     if (opt.icon.indexOf("<") === 0) { | ||||
|                         $(opt.icon).prependTo(op); | ||||
|                     } else if (opt.icon.indexOf("/") !== -1) { | ||||
|                         $('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px; height: 18px;"}).prependTo(op); | ||||
|                         $('<i>',{class:"red-ui-typedInput-icon", style:"mask-image: url("+opt.icon+"); -webkit-mask-image: url("+opt.icon+");"}).prependTo(op); | ||||
|                     } else { | ||||
|                         $('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(op); | ||||
|                     } | ||||
| @@ -579,7 +746,7 @@ | ||||
|             var height = relativeTo.height(); | ||||
|             var menuHeight = menu.height(); | ||||
|             var top = (height+pos.top); | ||||
|             if (top+menuHeight > $(window).height()) { | ||||
|             if (top+menuHeight-$(document).scrollTop() > $(window).height()) { | ||||
|                 top -= (top+menuHeight)-$(window).height()+5; | ||||
|             } | ||||
|             if (top < 0) { | ||||
| @@ -676,6 +843,7 @@ | ||||
|             var that = this; | ||||
|             var currentType = this.type(); | ||||
|             this.typeMap = {}; | ||||
|             var firstCall = (this.typeList === undefined); | ||||
|             this.typeList = types.map(function(opt) { | ||||
|                 var result; | ||||
|                 if (typeof opt === 'string') { | ||||
| @@ -688,8 +856,10 @@ | ||||
|             }); | ||||
|             if (this.typeList.length < 2) { | ||||
|                 this.selectTrigger.attr("tabindex", -1) | ||||
|                 this.selectTrigger.on("mousedown.red-ui-typedInput-focus-block", function(evt) { evt.preventDefault(); }) | ||||
|             } else { | ||||
|                 this.selectTrigger.attr("tabindex", 0) | ||||
|                 this.selectTrigger.off("mousedown.red-ui-typedInput-focus-block") | ||||
|             } | ||||
|             this.selectTrigger.toggleClass("disabled", this.typeList.length === 1); | ||||
|             this.selectTrigger.find(".fa-caret-down").toggle(this.typeList.length > 1) | ||||
| @@ -698,10 +868,19 @@ | ||||
|             } | ||||
|             this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) }); | ||||
|             if (currentType && !this.typeMap.hasOwnProperty(currentType)) { | ||||
|                 this.type(this.typeList[0].value); | ||||
|                 if (!firstCall) { | ||||
|                     this.type(this.typeList[0].value); | ||||
|                 } | ||||
|             } else { | ||||
|                 this.propertyType = null; | ||||
|                 this.type(currentType); | ||||
|                 if (!firstCall) { | ||||
|                     this.type(currentType); | ||||
|                 } | ||||
|             } | ||||
|             if (this.typeList.length === 1 && !this.typeList[0].icon && (!this.typeList[0].label || this.typeList[0].showLabel === false)) { | ||||
|                 this.selectTrigger.hide() | ||||
|             } else { | ||||
|                 this.selectTrigger.show() | ||||
|             } | ||||
|         }, | ||||
|         width: function(desiredWidth) { | ||||
| @@ -712,7 +891,10 @@ | ||||
|         }, | ||||
|         value: function(value) { | ||||
|             var that = this; | ||||
|             var opt = this.typeMap[this.propertyType]; | ||||
|             // If the default type has been set to an invalid type, then on first | ||||
|             // creation, the current propertyType will not exist. Default to an | ||||
|             // empty object on the assumption the corrent type will be set shortly | ||||
|             var opt = this.typeMap[this.propertyType] || {}; | ||||
|             if (!arguments.length) { | ||||
|                 var v = this.input.val(); | ||||
|                 if (opt.export) { | ||||
| @@ -720,27 +902,38 @@ | ||||
|                 } | ||||
|                 return v; | ||||
|             } else { | ||||
|                 if (this.options.debug) { console.log(this.identifier,"----- SET VALUE ------",value) } | ||||
|                 var selectedOption = []; | ||||
|                 var valueToCheck = value; | ||||
|                 if (opt.options) { | ||||
|                     var checkValues = [value]; | ||||
|                     if (opt.hasValue && opt.parse) { | ||||
|                         var parts = opt.parse(value); | ||||
|                         if (this.options.debug) { console.log(this.identifier,"new parse",parts) } | ||||
|                         value = parts.value; | ||||
|                         valueToCheck = parts.option || parts.value; | ||||
|                     } | ||||
|  | ||||
|                     var checkValues = [valueToCheck]; | ||||
|                     if (opt.multiple) { | ||||
|                         selectedOption = []; | ||||
|                         checkValues = value.split(","); | ||||
|                         checkValues = valueToCheck.split(","); | ||||
|                     } | ||||
|                     checkValues.forEach(function(value) { | ||||
|                     checkValues.forEach(function(valueToCheck) { | ||||
|                         for (var i=0;i<opt.options.length;i++) { | ||||
|                             var op = opt.options[i]; | ||||
|                             if (typeof op === "string") { | ||||
|                                 if (op === value || op === ""+value) { | ||||
|                                 if (op === valueToCheck || op === ""+valueToCheck) { | ||||
|                                     selectedOption.push(that.activeOptions[op]); | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } else if (op.value === value) { | ||||
|                             } else if (op.value === valueToCheck) { | ||||
|                                 selectedOption.push(op); | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                     }) | ||||
|                     if (this.options.debug) { console.log(this.identifier,"set value to",value) } | ||||
|  | ||||
|                     this.input.val(value); | ||||
|                     if (!opt.multiple) { | ||||
|                         if (selectedOption.length === 0) { | ||||
| @@ -765,9 +958,64 @@ | ||||
|                 return this.propertyType; | ||||
|             } else { | ||||
|                 var that = this; | ||||
|                 if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) } | ||||
|                 var previousValue = null; | ||||
|                 var opt = this.typeMap[type]; | ||||
|                 if (opt && this.propertyType !== type) { | ||||
|                     // If previousType is !null, then this is a change of the type, rather than the initialisation | ||||
|                     var previousType = this.typeMap[this.propertyType]; | ||||
|                     previousValue = this.input.val(); | ||||
|  | ||||
|                     if (previousType && this.typeChanged) { | ||||
|                         if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) } | ||||
|                         if (previousType.options && opt.hasValue !== true) { | ||||
|                             this.oldValues[previousType.value] = previousValue; | ||||
|                         } else if (previousType.hasValue === false) { | ||||
|                             this.oldValues[previousType.value] = previousValue; | ||||
|                         } else { | ||||
|                             this.oldValues["_"] = previousValue; | ||||
|                         } | ||||
|                         if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) { | ||||
|                             if (this.oldValues.hasOwnProperty(opt.value)) { | ||||
|                                 if (this.options.debug) { console.log(this.identifier,"restored previous (1)",this.oldValues[opt.value]) } | ||||
|                                 this.input.val(this.oldValues[opt.value]); | ||||
|                             } else if (opt.options) { | ||||
|                                 // No old value for the option type. | ||||
|                                 // It is possible code has called 'value' then 'type' | ||||
|                                 // to set the selected option. This is what the Inject/Switch/Change | ||||
|                                 // nodes did before 2.1. | ||||
|                                 // So we need to be careful to not reset the value if it is a valid option. | ||||
|                                 var validOptions = isOptionValueValid(opt,previousValue); | ||||
|                                 if (this.options.debug) { console.log(this.identifier,{previousValue,opt,validOptions}) } | ||||
|                                 if ((previousValue || previousValue === '') && validOptions) { | ||||
|                                     if (this.options.debug) { console.log(this.identifier,"restored previous (2)") } | ||||
|                                     this.input.val(previousValue); | ||||
|                                 } else { | ||||
|                                     if (typeof opt.default === "string") { | ||||
|                                         if (this.options.debug) { console.log(this.identifier,"restored previous (3)",opt.default) } | ||||
|                                         this.input.val(opt.default); | ||||
|                                     } else if (Array.isArray(opt.default)) { | ||||
|                                         if (this.options.debug) { console.log(this.identifier,"restored previous (4)",opt.default.join(",")) } | ||||
|                                         this.input.val(opt.default.join(",")) | ||||
|                                     } else { | ||||
|                                         if (this.options.debug) { console.log(this.identifier,"restored previous (5)") } | ||||
|                                         this.input.val(""); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 if (this.options.debug) { console.log(this.identifier,"restored default/blank",opt.default||"") } | ||||
|                                 this.input.val(opt.default||"") | ||||
|                             } | ||||
|                         } else { | ||||
|                             if (this.options.debug) { console.log(this.identifier,"restored old/default/blank") } | ||||
|                             this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||"")) | ||||
|                         } | ||||
|                         if (previousType.autoComplete) { | ||||
|                             this.input.autoComplete("destroy"); | ||||
|                         } | ||||
|                     } | ||||
|                     this.propertyType = type; | ||||
|                     this.typeChanged = true; | ||||
|                     if (this.typeField) { | ||||
|                         this.typeField.val(type); | ||||
|                     } | ||||
| @@ -778,10 +1026,7 @@ | ||||
|                             $(opt.icon).prependTo(this.selectLabel); | ||||
|                         } | ||||
|                         else if (opt.icon.indexOf("/") !== -1) { | ||||
|                             image = new Image(); | ||||
|                             image.name = opt.icon; | ||||
|                             image.src = mapDeprecatedIcon(opt.icon); | ||||
|                             $('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel); | ||||
|                             $('<i>',{class:"red-ui-typedInput-icon", style:"mask-image: url("+opt.icon+"); -webkit-mask-image: url("+opt.icon+"); margin-right: 4px;height: 18px;width:13px"}).prependTo(this.selectLabel); | ||||
|                         } | ||||
|                         else { | ||||
|                             $('<i>',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel); | ||||
| @@ -836,22 +1081,12 @@ | ||||
|  | ||||
|                             var op; | ||||
|                             if (!opt.hasValue) { | ||||
|                                 var validValue = false; | ||||
|                                 var currentVal = this.input.val(); | ||||
|                                 // Check the value is valid for the available options | ||||
|                                 var validValues = isOptionValueValid(opt,this.input.val()); | ||||
|                                 if (!opt.multiple) { | ||||
|                                     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) { | ||||
|                                     if (validValues) { | ||||
|                                         that._updateOptionSelectLabel(validValues) | ||||
|                                     } else { | ||||
|                                         op = opt.options[0]; | ||||
|                                         if (typeof op === "string") { | ||||
|                                             this.value(op); | ||||
| @@ -862,27 +1097,19 @@ | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     // Check to see if value is a valid csv of | ||||
|                                     // options. | ||||
|                                     var currentValues = {}; | ||||
|                                     currentVal.split(",").forEach(function(v) { | ||||
|                                         if (v) { | ||||
|                                             currentValues[v] = true; | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     for (var i=0;i<opt.options.length;i++) { | ||||
|                                         op = opt.options[i]; | ||||
|                                         delete currentValues[op.value||op]; | ||||
|                                     } | ||||
|                                     if (!$.isEmptyObject(currentValues)) { | ||||
|                                         // Invalid, set to default/empty | ||||
|                                         this.value((opt.default||[]).join(",")); | ||||
|                                     if (!validValues) { | ||||
|                                         validValues = (opt.default || []).map(function(v) { | ||||
|                                             return typeof v === "string"?v:v.value | ||||
|                                         }); | ||||
|                                         this.value(validValues.join(",")); | ||||
|                                     } | ||||
|                                     that._updateOptionSelectLabel(validValues); | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 var selectedOption = this.optionValue||opt.options[0]; | ||||
|                                 if (opt.parse) { | ||||
|                                     var parts = opt.parse(this.input.val(),selectedOption); | ||||
|                                     var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption | ||||
|                                     var parts = opt.parse(this.input.val(),selectedOptionObj); | ||||
|                                     if (parts.option) { | ||||
|                                         selectedOption = parts.option; | ||||
|                                         if (!this.activeOptions.hasOwnProperty(selectedOption)) { | ||||
| @@ -906,6 +1133,7 @@ | ||||
|                                         this._updateOptionSelectLabel(this.activeOptions[selectedOption]); | ||||
|                                     } | ||||
|                                 } else if (selectedOption) { | ||||
|                                     if (this.options.debug) { console.log(this.identifier,"HERE",{optionValue:selectedOption.value}) } | ||||
|                                     this.optionValue = selectedOption.value; | ||||
|                                     this._updateOptionSelectLabel(selectedOption); | ||||
|                                 } else { | ||||
| @@ -938,8 +1166,6 @@ | ||||
|                             this.input.attr('type',this.defaultInputType) | ||||
|                         } | ||||
|                         if (opt.hasValue === false) { | ||||
|                             this.oldValue = this.input.val(); | ||||
|                             this.input.val(""); | ||||
|                             this.elementDiv.hide(); | ||||
|                             this.valueLabelContainer.hide(); | ||||
|                         } else if (opt.valueLabel) { | ||||
| @@ -952,12 +1178,14 @@ | ||||
|                             this.elementDiv.hide(); | ||||
|                             opt.valueLabel.call(this,this.valueLabelContainer,this.input.val()); | ||||
|                         } else { | ||||
|                             if (this.oldValue !== undefined) { | ||||
|                                 this.input.val(this.oldValue); | ||||
|                                 delete this.oldValue; | ||||
|                             } | ||||
|                             this.valueLabelContainer.hide(); | ||||
|                             this.elementDiv.show(); | ||||
|                             if (opt.autoComplete) { | ||||
|                                 this.input.autoComplete({ | ||||
|                                     search: opt.autoComplete, | ||||
|                                     minLength: 0 | ||||
|                                 }) | ||||
|                             } | ||||
|                         } | ||||
|                         if (this.optionExpandButton) { | ||||
|                             if (opt.expand) { | ||||
| @@ -1042,6 +1270,9 @@ | ||||
|         }, | ||||
|         disabled: function() { | ||||
|             return this.uiSelect.attr("disabled") === "disabled"; | ||||
|         }, | ||||
|         focus: function() { | ||||
|             this.input.focus(); | ||||
|         } | ||||
|     }); | ||||
| })(jQuery); | ||||
|   | ||||
| @@ -319,192 +319,248 @@ RED.deploy = (function() { | ||||
|             },delta); | ||||
|         }); | ||||
|     } | ||||
|     function save(skipValidation,force) { | ||||
|         if (!$("#red-ui-header-button-deploy").hasClass("disabled")) { | ||||
|             if (!RED.user.hasPermission("flows.write")) { | ||||
|                 RED.notify(RED._("user.errors.deploy"),"error"); | ||||
|     function save(skipValidation, force) { | ||||
|         if ($("#red-ui-header-button-deploy").hasClass("disabled")) { | ||||
|             return; //deploy is disabled | ||||
|         } | ||||
|         if ($("#red-ui-header-shade").is(":visible")) { | ||||
|             return; //deploy is shaded | ||||
|         } | ||||
|         if (!RED.user.hasPermission("flows.write")) { | ||||
|             RED.notify(RED._("user.errors.deploy"), "error"); | ||||
|             return; | ||||
|         } | ||||
|         let hasUnusedConfig = false; | ||||
|         if (!skipValidation) { | ||||
|             let hasUnknown = false; | ||||
|             let hasInvalid = false; | ||||
|             const unknownNodes = []; | ||||
|             const invalidNodes = []; | ||||
|  | ||||
|             RED.nodes.eachConfig(function (node) { | ||||
|                 if (node.valid === undefined) { | ||||
|                     RED.editor.validateNode(node); | ||||
|                 } | ||||
|                 if (!node.valid && !node.d) { | ||||
|                     invalidNodes.push(getNodeInfo(node)); | ||||
|                 } | ||||
|                 if (node.type === "unknown") { | ||||
|                     if (unknownNodes.indexOf(node.name) == -1) { | ||||
|                         unknownNodes.push(node.name); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             RED.nodes.eachNode(function (node) { | ||||
|                 if (!node.valid && !node.d) { | ||||
|                     invalidNodes.push(getNodeInfo(node)); | ||||
|                 } | ||||
|                 if (node.type === "unknown") { | ||||
|                     if (unknownNodes.indexOf(node.name) == -1) { | ||||
|                         unknownNodes.push(node.name); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             hasUnknown = unknownNodes.length > 0; | ||||
|             hasInvalid = invalidNodes.length > 0; | ||||
|  | ||||
|             const unusedConfigNodes = []; | ||||
|             RED.nodes.eachConfig(function (node) { | ||||
|                 if ((node._def.hasUsers !== false) && (node.users.length === 0)) { | ||||
|                     unusedConfigNodes.push(getNodeInfo(node)); | ||||
|                     hasUnusedConfig = true; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             let showWarning = false; | ||||
|             let notificationMessage; | ||||
|             let notificationButtons = []; | ||||
|             let notification; | ||||
|             if (hasUnknown && !ignoreDeployWarnings.unknown) { | ||||
|                 showWarning = true; | ||||
|                 notificationMessage = "<p>" + RED._('deploy.confirm.unknown') + "</p>" + | ||||
|                     '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(unknownNodes).map(function (n) { return sanitize(n) }).join("</li><li>") + "</li></ul><p>" + | ||||
|                     RED._('deploy.confirm.confirm') + | ||||
|                     "</p>"; | ||||
|  | ||||
|                 notificationButtons = [ | ||||
|                     { | ||||
|                         text: RED._("deploy.unknownNodesButton"), | ||||
|                         class: "pull-left", | ||||
|                         click: function() { | ||||
|                             notification.close(); | ||||
|                             RED.actions.invoke("core:search","type:unknown "); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "red-ui-deploy-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; | ||||
|                 invalidNodes.sort(sortNodeInfo); | ||||
|  | ||||
|                 notificationMessage = "<p>" + RED._('deploy.confirm.improperlyConfigured') + "</p>" + | ||||
|                     '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(invalidNodes.map(function (A) { return sanitize((A.tab ? "[" + A.tab + "] " : "") + A.label + " (" + A.type + ")") })).join("</li><li>") + "</li></ul><p>" + | ||||
|                     RED._('deploy.confirm.confirm') + | ||||
|                     "</p>"; | ||||
|                 notificationButtons = [ | ||||
|                     { | ||||
|                         text: RED._("deploy.invalidNodesButton"), | ||||
|                         class: "pull-left", | ||||
|                         click: function() { | ||||
|                             notification.close(); | ||||
|                             RED.actions.invoke("core:search","is:invalid "); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "red-ui-deploy-dialog-confirm-deploy-deploy", | ||||
|                         text: RED._("deploy.confirm.button.confirm"), | ||||
|                         class: "primary", | ||||
|                         click: function () { | ||||
|                             save(true); | ||||
|                             notification.close(); | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|             } | ||||
|             if (showWarning) { | ||||
|                 notificationButtons.unshift( | ||||
|                     { | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function () { | ||||
|                             notification.close(); | ||||
|                         } | ||||
|                     } | ||||
|                 ); | ||||
|                 notification = RED.notify(notificationMessage, { | ||||
|                     modal: true, | ||||
|                     fixed: true, | ||||
|                     buttons: notificationButtons | ||||
|                 }); | ||||
|                 return; | ||||
|             } | ||||
|             if (!skipValidation) { | ||||
|                 var hasUnknown = false; | ||||
|                 var hasInvalid = false; | ||||
|                 var hasUnusedConfig = false; | ||||
|  | ||||
|                 var unknownNodes = []; | ||||
|                 var invalidNodes = []; | ||||
|  | ||||
|                 RED.nodes.eachNode(function(node) { | ||||
|                     if (!node.valid && !node.d) { | ||||
|                         invalidNodes.push(getNodeInfo(node)); | ||||
|                     } | ||||
|                     if (node.type === "unknown") { | ||||
|                         if (unknownNodes.indexOf(node.name) == -1) { | ||||
|                             unknownNodes.push(node.name); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|                 hasUnknown = unknownNodes.length > 0; | ||||
|                 hasInvalid = invalidNodes.length > 0; | ||||
|  | ||||
|                 var unusedConfigNodes = []; | ||||
|                 RED.nodes.eachConfig(function(node) { | ||||
|                     if ((node._def.hasUsers !== false) && (node.users.length === 0)) { | ||||
|                         unusedConfigNodes.push(getNodeInfo(node)); | ||||
|                         hasUnusedConfig = true; | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 var showWarning = false; | ||||
|                 var notificationMessage; | ||||
|                 var notificationButtons = []; | ||||
|                 var notification; | ||||
|                 if (hasUnknown && !ignoreDeployWarnings.unknown) { | ||||
|                     showWarning = true; | ||||
|                     notificationMessage = "<p>"+RED._('deploy.confirm.unknown')+"</p>"+ | ||||
|                         '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(unknownNodes).map(function(n) { return sanitize(n) }).join("</li><li>")+"</li></ul><p>"+ | ||||
|                         RED._('deploy.confirm.confirm')+ | ||||
|                         "</p>"; | ||||
|  | ||||
|                     notificationButtons= [ | ||||
|                         { | ||||
|                             id: "red-ui-deploy-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; | ||||
|                     invalidNodes.sort(sortNodeInfo); | ||||
|  | ||||
|                     notificationMessage = "<p>"+RED._('deploy.confirm.improperlyConfigured')+"</p>"+ | ||||
|                         '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(invalidNodes.map(function(A) { return sanitize( (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")")})).join("</li><li>")+"</li></ul><p>"+ | ||||
|                         RED._('deploy.confirm.confirm')+ | ||||
|                         "</p>"; | ||||
|                     notificationButtons= [ | ||||
|                         { | ||||
|                             id: "red-ui-deploy-dialog-confirm-deploy-deploy", | ||||
|                             text: RED._("deploy.confirm.button.confirm"), | ||||
|                             class: "primary", | ||||
|                             click: function() { | ||||
|                                 save(true); | ||||
|                                 notification.close(); | ||||
|                             } | ||||
|                         } | ||||
|                     ]; | ||||
|                 } | ||||
|                 if (showWarning) { | ||||
|                     notificationButtons.unshift( | ||||
|                         { | ||||
|                             text: RED._("common.label.cancel"), | ||||
|                             click: function() { | ||||
|                                 notification.close(); | ||||
|                             } | ||||
|                         } | ||||
|                     ); | ||||
|                     notification = RED.notify(notificationMessage,{ | ||||
|                         modal: true, | ||||
|                         fixed: true, | ||||
|                         buttons:notificationButtons | ||||
|                     }); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var nns = RED.nodes.createCompleteNodeSet(); | ||||
|  | ||||
|             var startTime = Date.now(); | ||||
|             $(".red-ui-deploy-button-content").css('opacity',0); | ||||
|             $(".red-ui-deploy-button-spinner").show(); | ||||
|             $("#red-ui-header-button-deploy").addClass("disabled"); | ||||
|  | ||||
|             var data = {flows:nns}; | ||||
|  | ||||
|             if (!force) { | ||||
|                 data.rev = RED.nodes.version(); | ||||
|             } | ||||
|  | ||||
|             deployInflight = true; | ||||
|             $("#red-ui-header-shade").show(); | ||||
|             $("#red-ui-editor-shade").show(); | ||||
|             $("#red-ui-palette-shade").show(); | ||||
|             $("#red-ui-sidebar-shade").show(); | ||||
|             $.ajax({ | ||||
|                 url:"flows", | ||||
|                 type: "POST", | ||||
|                 data: JSON.stringify(data), | ||||
|                 contentType: "application/json; charset=utf-8", | ||||
|                 headers: { | ||||
|                     "Node-RED-Deployment-Type":deploymentType | ||||
|                 } | ||||
|             }).done(function(data,textStatus,xhr) { | ||||
|                 RED.nodes.dirty(false); | ||||
|                 RED.nodes.version(data.rev); | ||||
|                 RED.nodes.originalFlow(nns); | ||||
|                 if (hasUnusedConfig) { | ||||
|                     RED.notify( | ||||
|                     '<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('<p>'+RED._("deploy.successfulDeploy")+'</p>',"success"); | ||||
|                 } | ||||
|                 RED.nodes.eachNode(function(node) { | ||||
|                     if (node.changed) { | ||||
|                         node.dirty = true; | ||||
|                         node.changed = false; | ||||
|                     } | ||||
|                     if (node.moved) { | ||||
|                         node.dirty = true; | ||||
|                         node.moved = false; | ||||
|                     } | ||||
|                     if(node.credentials) { | ||||
|                         delete node.credentials; | ||||
|                     } | ||||
|                 }); | ||||
|                 RED.nodes.eachConfig(function (confNode) { | ||||
|                     confNode.changed = false; | ||||
|                     if (confNode.credentials) { | ||||
|                         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(); | ||||
|                 RED.events.emit("deploy"); | ||||
|             }).fail(function(xhr,textStatus,err) { | ||||
|                 RED.nodes.dirty(true); | ||||
|                 $("#red-ui-header-button-deploy").removeClass("disabled"); | ||||
|                 if (xhr.status === 401) { | ||||
|                     RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error"); | ||||
|                 } else if (xhr.status === 409) { | ||||
|                     resolveConflict(nns, true); | ||||
|                 } else if (xhr.responseText) { | ||||
|                     RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error"); | ||||
|                 } else { | ||||
|                     RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error"); | ||||
|                 } | ||||
|             }).always(function() { | ||||
|                 deployInflight = false; | ||||
|                 var delta = Math.max(0,300-(Date.now()-startTime)); | ||||
|                 setTimeout(function() { | ||||
|                     $(".red-ui-deploy-button-content").css('opacity',1); | ||||
|                     $(".red-ui-deploy-button-spinner").hide(); | ||||
|                     $("#red-ui-header-shade").hide(); | ||||
|                     $("#red-ui-editor-shade").hide(); | ||||
|                     $("#red-ui-palette-shade").hide(); | ||||
|                     $("#red-ui-sidebar-shade").hide(); | ||||
|                 },delta); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         const nns = RED.nodes.createCompleteNodeSet(); | ||||
|         const startTime = Date.now(); | ||||
|  | ||||
|         $(".red-ui-deploy-button-content").css('opacity', 0); | ||||
|         $(".red-ui-deploy-button-spinner").show(); | ||||
|         $("#red-ui-header-button-deploy").addClass("disabled"); | ||||
|  | ||||
|         const data = { flows: nns }; | ||||
|  | ||||
|         if (!force) { | ||||
|             data.rev = RED.nodes.version(); | ||||
|         } | ||||
|  | ||||
|         deployInflight = true; | ||||
|         $("#red-ui-header-shade").show(); | ||||
|         $("#red-ui-editor-shade").show(); | ||||
|         $("#red-ui-palette-shade").show(); | ||||
|         $("#red-ui-sidebar-shade").show(); | ||||
|         $.ajax({ | ||||
|             url: "flows", | ||||
|             type: "POST", | ||||
|             data: JSON.stringify(data), | ||||
|             contentType: "application/json; charset=utf-8", | ||||
|             headers: { | ||||
|                 "Node-RED-Deployment-Type": deploymentType | ||||
|             } | ||||
|         }).done(function (data, textStatus, xhr) { | ||||
|             RED.nodes.dirty(false); | ||||
|             RED.nodes.version(data.rev); | ||||
|             RED.nodes.originalFlow(nns); | ||||
|             if (hasUnusedConfig) { | ||||
|                 let notification; | ||||
|                 const opts = { | ||||
|                     type: "success", | ||||
|                     fixed: false, | ||||
|                     timeout: 6000, | ||||
|                     buttons: [ | ||||
|                         { | ||||
|                             text: RED._("deploy.unusedConfigNodesButton"), | ||||
|                             class: "pull-left", | ||||
|                             click: function() { | ||||
|                                 notification.close(); | ||||
|                                 RED.actions.invoke("core:search","is:config is:unused "); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             text: RED._("common.label.close"), | ||||
|                             class: "primary", | ||||
|                             click: function () { | ||||
|                                 save(true); | ||||
|                                 notification.close(); | ||||
|                             } | ||||
|                         } | ||||
|                     ] | ||||
|                 } | ||||
|                 notification = RED.notify( | ||||
|                     '<p>' + RED._("deploy.successfulDeploy") + '</p>' + | ||||
|                     '<p>' + RED._("deploy.unusedConfigNodes") + '</p>', opts); | ||||
|             } else { | ||||
|                 RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success"); | ||||
|             } | ||||
|             RED.nodes.eachNode(function (node) { | ||||
|                 if (node.changed) { | ||||
|                     node.dirty = true; | ||||
|                     node.changed = false; | ||||
|                 } | ||||
|                 if (node.moved) { | ||||
|                     node.dirty = true; | ||||
|                     node.moved = false; | ||||
|                 } | ||||
|                 if (node.credentials) { | ||||
|                     delete node.credentials; | ||||
|                 } | ||||
|             }); | ||||
|             RED.nodes.eachConfig(function (confNode) { | ||||
|                 confNode.changed = false; | ||||
|                 if (confNode.credentials) { | ||||
|                     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(); | ||||
|             RED.events.emit("deploy"); | ||||
|         }).fail(function (xhr, textStatus, err) { | ||||
|             RED.nodes.dirty(true); | ||||
|             $("#red-ui-header-button-deploy").removeClass("disabled"); | ||||
|             if (xhr.status === 401) { | ||||
|                 RED.notify(RED._("deploy.deployFailed", { message: RED._("user.notAuthorized") }), "error"); | ||||
|             } else if (xhr.status === 409) { | ||||
|                 resolveConflict(nns, true); | ||||
|             } else if (xhr.responseText) { | ||||
|                 RED.notify(RED._("deploy.deployFailed", { message: xhr.responseText }), "error"); | ||||
|             } else { | ||||
|                 RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error"); | ||||
|             } | ||||
|         }).always(function () { | ||||
|             deployInflight = false; | ||||
|             const delta = Math.max(0, 300 - (Date.now() - startTime)); | ||||
|             setTimeout(function () { | ||||
|                 $(".red-ui-deploy-button-content").css('opacity', 1); | ||||
|                 $(".red-ui-deploy-button-spinner").hide(); | ||||
|                 $("#red-ui-header-shade").hide(); | ||||
|                 $("#red-ui-editor-shade").hide(); | ||||
|                 $("#red-ui-palette-shade").hide(); | ||||
|                 $("#red-ui-sidebar-shade").hide(); | ||||
|             }, delta); | ||||
|         }); | ||||
|     } | ||||
|     return { | ||||
|         init: init, | ||||
|   | ||||
							
								
								
									
										61
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
|  | ||||
| RED.diagnostics = (function () { | ||||
|  | ||||
|     function init() { | ||||
|         if (RED.settings.get('diagnostics.ui', true) === false) { | ||||
|             return; | ||||
|         } | ||||
|         RED.actions.add("core:show-system-info", function () { show(); }); | ||||
|     } | ||||
|  | ||||
|     function show() { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept": "application/json" | ||||
|             }, | ||||
|             cache: false, | ||||
|             url: 'diagnostics', | ||||
|             success: function (data) { | ||||
|                 var json = JSON.stringify(data || {}, "", 4); | ||||
|                 if (json === "{}") { | ||||
|                     json = "{\n\n}"; | ||||
|                 } | ||||
|                 RED.editor.editJSON({ | ||||
|                     title: RED._('diagnostics.title'), | ||||
|                     value: json, | ||||
|                     requireValid: true, | ||||
|                     readOnly: true, | ||||
|                     toolbarButtons: [ | ||||
|                         { | ||||
|                             text: RED._('clipboard.export.copy'), | ||||
|                             icon: 'fa fa-copy', | ||||
|                             click: function () { | ||||
|                                 RED.clipboard.copyText(json, $(this), RED._('clipboard.copyMessageValue')) | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             text: RED._('clipboard.download'), | ||||
|                             icon: 'fa fa-download', | ||||
|                             click: function () { | ||||
|                                 var element = document.createElement('a'); | ||||
|                                 element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(json)); | ||||
|                                 element.setAttribute('download', "system-info.json"); | ||||
|                                 element.style.display = 'none'; | ||||
|                                 document.body.appendChild(element); | ||||
|                                 element.click(); | ||||
|                                 document.body.removeChild(element); | ||||
|                             } | ||||
|                         }, | ||||
|                     ] | ||||
|                 }); | ||||
|             }, | ||||
|             error: function (jqXHR, textStatus, errorThrown) { | ||||
|                 console.log("Unexpected error loading system info:", jqXHR.status, textStatus, errorThrown); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|     }; | ||||
| })(); | ||||
| @@ -554,6 +554,8 @@ RED.diff = (function() { | ||||
|                     color: "#DDAA99", | ||||
|                     defaults:{name:{value:""}} | ||||
|                 } | ||||
|             } else if (node.type === "group") { | ||||
|                 def = RED.group.def; | ||||
|             } else { | ||||
|                 def = {}; | ||||
|             } | ||||
| @@ -763,16 +765,15 @@ RED.diff = (function() { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         if (node.hasOwnProperty('x')) { | ||||
|             if (localNode) { | ||||
|                 if (localNode.x !== node.x || localNode.y !== node.y) { | ||||
|                 if (localNode.x !== node.x || localNode.y !== node.y || localNode.w !== node.w || localNode.h !== node.h ) { | ||||
|                     localChanged = true; | ||||
|                     localChanges++; | ||||
|                 } | ||||
|             } | ||||
|             if (remoteNode) { | ||||
|                 if (remoteNode.x !== node.x || remoteNode.y !== node.y) { | ||||
|                 if (remoteNode.x !== node.x || remoteNode.y !== node.y|| remoteNode.w !== node.w || remoteNode.h !== node.h) { | ||||
|                     remoteChanged = true; | ||||
|                     remoteChanges++; | ||||
|                 } | ||||
| @@ -790,7 +791,12 @@ RED.diff = (function() { | ||||
|                 localCell.addClass("red-ui-diff-status-"+(localChanged?"changed":"unchanged")); | ||||
|                 $('<span class="red-ui-diff-status">'+(localChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(localCell); | ||||
|                 element = $('<span class="red-ui-diff-list-element"></span>').appendTo(localCell); | ||||
|                 propertyElements['local.position'] = RED.utils.createObjectElement({x:localNode.x,y:localNode.y}, | ||||
|                 var localPosition = {x:localNode.x,y:localNode.y}; | ||||
|                 if (localNode.hasOwnProperty('w')) { | ||||
|                     localPosition.w = localNode.w; | ||||
|                     localPosition.h = localNode.h; | ||||
|                 } | ||||
|                 propertyElements['local.position'] = RED.utils.createObjectElement(localPosition, | ||||
|                     { | ||||
|                         path: "position", | ||||
|                         exposeApi: true, | ||||
| @@ -811,7 +817,12 @@ RED.diff = (function() { | ||||
|                 if (remoteNode) { | ||||
|                     $('<span class="red-ui-diff-status">'+(remoteChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(remoteCell); | ||||
|                     element = $('<span class="red-ui-diff-list-element"></span>').appendTo(remoteCell); | ||||
|                     propertyElements['remote.position'] = RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y}, | ||||
|                     var remotePosition = {x:remoteNode.x,y:remoteNode.y}; | ||||
|                     if (remoteNode.hasOwnProperty('w')) { | ||||
|                         remotePosition.w = remoteNode.w; | ||||
|                         remotePosition.h = remoteNode.h; | ||||
|                     } | ||||
|                     propertyElements['remote.position'] = RED.utils.createObjectElement(remotePosition, | ||||
|                         { | ||||
|                             path: "position", | ||||
|                             exposeApi: true, | ||||
| @@ -883,11 +894,11 @@ RED.diff = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); | ||||
|         var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='w'&&p!=='h'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); | ||||
|         if (def.defaults) { | ||||
|             properties = properties.concat(Object.keys(def.defaults)); | ||||
|         } | ||||
|         if (node.type !== 'tab') { | ||||
|         if (node.type !== 'tab' && node.type !== "group") { | ||||
|             properties = properties.concat(['inputLabels','outputLabels']); | ||||
|         } | ||||
|         if ( ((localNode && localNode.hasOwnProperty('icon')) || (remoteNode && remoteNode.hasOwnProperty('icon'))) && | ||||
| @@ -1376,6 +1387,7 @@ RED.diff = (function() { | ||||
|  | ||||
|     function mergeDiff(diff) { | ||||
|         //console.log(diff); | ||||
|         var selectedTab = RED.workspaces.active(); | ||||
|         var appliedDiff = applyDiff(diff); | ||||
|  | ||||
|         var newConfig = appliedDiff.config; | ||||
| @@ -1426,6 +1438,7 @@ RED.diff = (function() { | ||||
|         RED.view.redraw(true); | ||||
|         RED.palette.refresh(); | ||||
|         RED.workspaces.refresh(); | ||||
|         RED.workspaces.show(selectedTab, true); | ||||
|         RED.sidebar.config.refresh(); | ||||
|     } | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -47,6 +47,7 @@ | ||||
|     var definition = { | ||||
|         show: function(options) { | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_buffer" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
| @@ -60,12 +61,14 @@ | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: "inherit", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if (onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -74,7 +77,8 @@ | ||||
|                         text: RED._("common.label.done"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                             onComplete(JSON.stringify(bufferBinValue)); | ||||
|                             bufferStringEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(JSON.stringify(bufferBinValue),null,bufferStringEditor); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -86,19 +90,20 @@ | ||||
|                     } | ||||
|                 }, | ||||
|                 open: function(tray) { | ||||
|                     var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|  | ||||
|                     bufferStringEditor = RED.editor.createEditor({ | ||||
|                         id: 'red-ui-editor-type-buffer-str', | ||||
|                         value: "", | ||||
|                         value: value||"", | ||||
|                         stateId: RED.editor.generateViewStateId("buffer", options, ""), | ||||
|                         focus: true, | ||||
|                         mode:"ace/mode/text" | ||||
|                     }); | ||||
|                     bufferStringEditor.getSession().setValue(value||"",-1); | ||||
|  | ||||
|                     bufferBinEditor = RED.editor.createEditor({ | ||||
|                         id: 'red-ui-editor-type-buffer-bin', | ||||
|                         value: "", | ||||
|                         stateId: false, | ||||
|                         focus: false, | ||||
|                         mode:"ace/mode/text", | ||||
|                         readOnly: true | ||||
|                     }); | ||||
|   | ||||
							
								
								
									
										107
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| /** | ||||
|  * @namespace RED.editor.codeEditor | ||||
|  */ | ||||
|  RED.editor.codeEditor = (function() { | ||||
|  | ||||
|     const MONACO = "monaco"; | ||||
|     const ACE = "ace"; | ||||
|     const defaultEditor = ACE; | ||||
|     const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} }; | ||||
|     var selectedCodeEditor = null; | ||||
|     var initialised = false; | ||||
|  | ||||
|     function init() { | ||||
|         var codeEditorSettings = RED.editor.codeEditor.settings; | ||||
|         var editorChoice = codeEditorSettings.lib === MONACO ? MONACO : ACE; | ||||
|         try { | ||||
|             var browser = RED.utils.getBrowserInfo(); | ||||
|             selectedCodeEditor = RED.editor.codeEditor[editorChoice]; | ||||
|             //fall back to default code editor if there are any issues | ||||
|             if (!selectedCodeEditor || (editorChoice === MONACO && (browser.ie || !window.monaco))) { | ||||
|                 selectedCodeEditor = RED.editor.codeEditor[defaultEditor]; | ||||
|             } | ||||
|             initialised = selectedCodeEditor.init(); | ||||
|         } catch (error) { | ||||
|             selectedCodeEditor = null; | ||||
|             console.warn("Problem initialising '" + editorChoice + "' code editor", error); | ||||
|         } | ||||
|         if(!initialised) { | ||||
|             selectedCodeEditor = RED.editor.codeEditor[defaultEditor]; | ||||
|             initialised = selectedCodeEditor.init(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function create(options) { | ||||
|         //TODO: (quandry - for consideration)  | ||||
|         // Below, I had to create a hidden element if options.id || options.element is not in the DOM | ||||
|         // I have seen 1 node calling  `this.editor = RED.editor.createEditor()` with an  | ||||
|         // invalid (non existing html element selector) (e.g. node-red-contrib-components does this) | ||||
|         // This causes monaco to throw an error when attempting to hook up its events to the dom  & the rest of the 'oneditperapre'  | ||||
|         // code is thus skipped.  | ||||
|         // In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI) | ||||
|         // Because one (or more) contrib nodes have left this bad code in place, how would we handle this? | ||||
|         // For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur. | ||||
|         // IMO, we should warn and exit as it is a coding error by the contrib author. | ||||
|  | ||||
|         if (!options) { | ||||
|             console.warn("createEditor() options are missing"); | ||||
|             options = {}; | ||||
|         } | ||||
|  | ||||
|         if (this.editor.type === MONACO) { | ||||
|             // compatibility (see above note) | ||||
|             if (!options.element && !options.id) { | ||||
|                 options.id = 'node-backwards-compatability-dummy-editor'; | ||||
|             } | ||||
|             options.element = options.element || $("#" + options.id)[0]; | ||||
|             if (!options.element) { | ||||
|                 console.warn("createEditor() options.element or options.id is not valid", options); | ||||
|                 $("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />'); | ||||
|             } | ||||
|             return this.editor.create(options); | ||||
|         } else { | ||||
|             return this.editor.create(options);//fallback to ACE | ||||
|         } | ||||
|     } | ||||
|   | ||||
|     return { | ||||
|         init: init, | ||||
|         /** | ||||
|          * Get editor settings object | ||||
|          * @memberof RED.editor.codeEditor | ||||
|          */ | ||||
|         get settings() { | ||||
|           return RED.settings.get('codeEditor') || DEFAULT_SETTINGS; | ||||
|         }, | ||||
|         /** | ||||
|          * Get user selected code editor | ||||
|          * @return {string} Returns  | ||||
|          * @memberof RED.editor.codeEditor | ||||
|          */ | ||||
|         get editor() { | ||||
|             return selectedCodeEditor; | ||||
|         }, | ||||
|         /** | ||||
|          * Create a editor ui component | ||||
|          * @param {object} options - the editor options | ||||
|          * @memberof RED.editor.codeEditor | ||||
|          */ | ||||
|         create: create | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										205
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/ace.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/ace.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| /* | ||||
| * 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. | ||||
| **/ | ||||
|  | ||||
| /** | ||||
| * @namespace RED.editor.codeEditor.ace | ||||
| */ | ||||
| RED.editor.codeEditor.ace = (function() { | ||||
|  | ||||
|     const type = "ace"; | ||||
|     var initialised = false; | ||||
|     var initOptions = {}; | ||||
|  | ||||
|     function init(options) { | ||||
|         initOptions = options || {};  | ||||
|         initialised = true; | ||||
|         return initialised; | ||||
|     } | ||||
|  | ||||
|     function create(options) { | ||||
|         var editorSettings = RED.editor.codeEditor.settings || {}; | ||||
|         var el = options.element || $("#"+options.id)[0]; | ||||
|         var toolbarRow = $("<div>").appendTo(el); | ||||
|         el = $("<div>").appendTo(el).addClass("red-ui-editor-text-container")[0]; | ||||
|         var editor = window.ace.edit(el); | ||||
|         editor.setTheme(editorSettings.theme || initOptions.theme || "ace/theme/tomorrow"); | ||||
|         var session = editor.getSession(); | ||||
|         session.on("changeAnnotation", function () { | ||||
|             var annotations = session.getAnnotations() || []; | ||||
|             var i = annotations.length; | ||||
|             var len = annotations.length; | ||||
|             while (i--) { | ||||
|                 if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); } | ||||
|                 else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); } | ||||
|             } | ||||
|             if (len > annotations.length) { session.setAnnotations(annotations); } | ||||
|         }); | ||||
|         if (options.mode) { | ||||
|             session.setMode(options.mode); | ||||
|         } | ||||
|         if (options.foldStyle) { | ||||
|             session.setFoldStyle(options.foldStyle); | ||||
|         } else { | ||||
|             session.setFoldStyle('markbeginend'); | ||||
|         } | ||||
|         if (options.options) { | ||||
|             editor.setOptions(options.options); | ||||
|         } else { | ||||
|             editor.setOptions({ | ||||
|                 enableBasicAutocompletion:true, | ||||
|                 enableSnippets:true, | ||||
|                 tooltipFollowsMouse: false | ||||
|             }); | ||||
|         } | ||||
|         if (options.readOnly) { | ||||
|             editor.setOption('readOnly',options.readOnly); | ||||
|             editor.container.classList.add("ace_read-only"); | ||||
|         } | ||||
|         if (options.hasOwnProperty('lineNumbers')) { | ||||
|             editor.renderer.setOption('showGutter',options.lineNumbers); | ||||
|         } | ||||
|         editor.$blockScrolling = Infinity; | ||||
|         if (options.value) { | ||||
|             session.setValue(options.value,-1); | ||||
|         } | ||||
|         if (options.globals) { | ||||
|             setTimeout(function() { | ||||
|                 if (!!session.$worker) { | ||||
|                     session.$worker.send("setOptions", [{globals: options.globals, maxerr:1000}]); | ||||
|                 } | ||||
|             },100); | ||||
|         } | ||||
|         if (!options.stateId && options.stateId !== false) { | ||||
|             options.stateId = RED.editor.generateViewStateId("ace", options, (options.mode || options.title).split("/").pop()); | ||||
|         } | ||||
|         if (options.mode === 'ace/mode/markdown') { | ||||
|             $(el).addClass("red-ui-editor-text-container-toolbar"); | ||||
|             editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor); | ||||
|             if (options.expandable !== false) { | ||||
|                 var expandButton = $('<button type="button" class="red-ui-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar); | ||||
|                 RED.popover.tooltip(expandButton, RED._("markdownEditor.expand")); | ||||
|                 expandButton.on("click", function(e) { | ||||
|                     e.preventDefault(); | ||||
|                     var value = editor.getValue(); | ||||
|                     RED.editor.editMarkdown({ | ||||
|                         value: value, | ||||
|                         width: "Infinity", | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true, | ||||
|                         cancel: function () { | ||||
|                             editor.focus(); | ||||
|                         }, | ||||
|                         complete: function(v,cursor) { | ||||
|                             editor.setValue(v, -1); | ||||
|                             setTimeout(function() { | ||||
|                                 editor.restoreView(); | ||||
|                                 editor.focus(); | ||||
|                             },300); | ||||
|                         } | ||||
|                     }) | ||||
|                 }); | ||||
|             } | ||||
|             var helpButton = $('<button type="button" class="red-ui-editor-text-help red-ui-button red-ui-button-small"><i class="fa fa-question"></i></button>').appendTo($(el).parent()); | ||||
|             RED.popover.create({ | ||||
|                 target: helpButton, | ||||
|                 trigger: 'click', | ||||
|                 size: "small", | ||||
|                 direction: "left", | ||||
|                 content: RED._("markdownEditor.format"), | ||||
|                 autoClose: 50 | ||||
|             }); | ||||
|             session.setUseWrapMode(true); | ||||
|         } | ||||
|         editor._destroy = editor.destroy; | ||||
|         editor.destroy = function() { | ||||
|             try { | ||||
|                 editor.saveView(); | ||||
|                 editor._initState = null; | ||||
|                 this._destroy(); | ||||
|             } catch (e) { } | ||||
|             $(el).remove(); | ||||
|             $(toolbarRow).remove(); | ||||
|         } | ||||
|         editor.on("blur", function () { | ||||
|             editor.focusMemory = false; | ||||
|             editor.saveView(); | ||||
|         }) | ||||
|         editor.on("focus", function () { | ||||
|             if (editor._initState) { | ||||
|                 editor.restoreView(editor._initState); | ||||
|                 editor._initState = null; | ||||
|             } | ||||
|         }) | ||||
|         editor.getView = function () { | ||||
|             var session = editor.getSession(); | ||||
|             return { | ||||
|                 selection: session.selection.toJSON(), | ||||
|                 scrollTop: session.getScrollTop(), | ||||
|                 scrollLeft: session.getScrollLeft(), | ||||
|                 options: session.getOptions() | ||||
|             } | ||||
|         } | ||||
|         editor.saveView = function () { | ||||
|             if (!options.stateId) { return; } //only possible if created with a unique stateId | ||||
|             window._editorStateAce = window._editorStateAce || {}; | ||||
|             var state = editor.getView(); | ||||
|             window._editorStateAce[options.stateId] = state; | ||||
|             return state; | ||||
|         } | ||||
|         editor.restoreView = function (state) { | ||||
|             if (!options.stateId) { return; } //only possible if created with a unique stateId | ||||
|             window._editorStateAce = window._editorStateAce || {}; | ||||
|             var _state = state || window._editorStateAce[options.stateId]; | ||||
|             if (!_state) { return; } //no view state available | ||||
|             try { | ||||
|                 var session = editor.getSession(); | ||||
|                 session.setOptions(_state.options); | ||||
|                 session.selection.fromJSON(_state.selection); | ||||
|                 session.setScrollTop(_state.scrollTop); | ||||
|                 session.setScrollLeft(_state.scrollLeft); | ||||
|                 editor._initState = _state; | ||||
|             } catch (error) { | ||||
|                 delete window._editorStateMonaco[options.stateId]; | ||||
|             } | ||||
|         }; | ||||
|         editor.restoreView(); | ||||
|         editor.type = type; | ||||
|         return editor; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         /** | ||||
|          * Editor type | ||||
|          * @memberof RED.editor.codeEditor.ace | ||||
|          */ | ||||
|          get type() { return type; }, | ||||
|          /** | ||||
|           * Editor initialised | ||||
|          * @memberof RED.editor.codeEditor.ace | ||||
|          */ | ||||
|         get initialised() { return initialised; }, | ||||
|         /** | ||||
|          * Initialise code editor | ||||
|          * @param {object} options - initialisation options | ||||
|          * @memberof RED.editor.codeEditor.ace | ||||
|          */ | ||||
|          init: init, | ||||
|          /** | ||||
|           * Create a code editor | ||||
|           * @param {object} options - the editor options | ||||
|           * @memberof RED.editor.codeEditor.ace | ||||
|           */ | ||||
|          create: create | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										1448
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1448
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,4 +1,4 @@ | ||||
| RED.colorPicker = (function() { | ||||
| RED.editor.colorPicker = RED.colorPicker = (function() { | ||||
| 
 | ||||
|     function create(options) { | ||||
|         var color = options.value; | ||||
							
								
								
									
										616
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										616
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,616 @@ | ||||
| RED.editor.envVarList = (function() { | ||||
|  | ||||
|     var currentLocale = 'en-US'; | ||||
|     var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env']; | ||||
|     var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred']; | ||||
|  | ||||
|     /** | ||||
|      * Create env var edit interface | ||||
|      * @param container - container | ||||
|      * @param node - subflow node | ||||
|      */ | ||||
|     function buildPropertiesList(envContainer, node) { | ||||
|  | ||||
|         var isTemplateNode = (node.type === "subflow"); | ||||
|  | ||||
|         envContainer | ||||
|             .css({ | ||||
|                 'min-height':'150px', | ||||
|                 'min-width':'450px' | ||||
|             }) | ||||
|             .editableList({ | ||||
|                 header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined, | ||||
|                 addItem: function(container, i, opt) { | ||||
|                     // If this is an instance node, these are properties unique to | ||||
|                     // this instance - ie opt.parent will not be defined. | ||||
|  | ||||
|                     if (isTemplateNode) { | ||||
|                         container.addClass("red-ui-editor-subflow-env-editable") | ||||
|                     } | ||||
|  | ||||
|                     var envRow = $('<div/>').appendTo(container); | ||||
|                     var nameField = null; | ||||
|                     var valueField = null; | ||||
|  | ||||
|                     nameField = $('<input/>', { | ||||
|                         class: "node-input-env-name", | ||||
|                         type: "text", | ||||
|                         placeholder: RED._("common.label.name") | ||||
|                     }).attr("autocomplete","disable").appendTo(envRow).val(opt.name); | ||||
|                     valueField = $('<input/>',{ | ||||
|                         style: "width:100%", | ||||
|                         class: "node-input-env-value", | ||||
|                         type: "text", | ||||
|                     }).attr("autocomplete","disable").appendTo(envRow) | ||||
|                     valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED}); | ||||
|                     valueField.typedInput('type', opt.type); | ||||
|                     if (opt.type === "cred") { | ||||
|                         if (opt.value) { | ||||
|                             valueField.typedInput('value', opt.value); | ||||
|                         } else if (node.credentials && node.credentials[opt.name]) { | ||||
|                             valueField.typedInput('value', node.credentials[opt.name]); | ||||
|                         } else if (node.credentials && node.credentials['has_'+opt.name]) { | ||||
|                             valueField.typedInput('value', "__PWRD__"); | ||||
|                         } else { | ||||
|                             valueField.typedInput('value', ""); | ||||
|                         } | ||||
|                     } else { | ||||
|                         valueField.typedInput('value', opt.value); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     opt.nameField = nameField; | ||||
|                     opt.valueField = valueField; | ||||
|  | ||||
|                     var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow); | ||||
|                     $('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton); | ||||
|                     var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove")); | ||||
|                     actionButton.on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         removeTip.close(); | ||||
|                         container.parent().addClass("red-ui-editableList-item-deleting") | ||||
|                         container.fadeOut(300, function() { | ||||
|                             envContainer.editableList('removeItem',opt); | ||||
|                         }); | ||||
|                     }); | ||||
|  | ||||
|                     if (isTemplateNode) { | ||||
|                         // Add the UI customisation row | ||||
|                         // if `opt.ui` does not exist, then apply defaults. If these | ||||
|                         // defaults do not change then they will get stripped off | ||||
|                         // before saving. | ||||
|                         if (opt.type === 'cred') { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "", | ||||
|                                 type: "cred" | ||||
|                             } | ||||
|                             opt.ui.type = "cred"; | ||||
|                         } else { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "", | ||||
|                                 type: "input", | ||||
|                                 opts: {types:DEFAULT_ENV_TYPE_LIST} | ||||
|                             } | ||||
|                         } | ||||
|                         opt.ui.label = opt.ui.label || {}; | ||||
|                         opt.ui.type = opt.ui.type || "input"; | ||||
|  | ||||
|                         var uiRow = $('<div/>').appendTo(container).hide(); | ||||
|                         // save current info for reverting on cancel | ||||
|                         // var copy = $.extend(true, {}, ui); | ||||
|  | ||||
|                          $('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) { | ||||
|                             evt.preventDefault(); | ||||
|                             if ($(this).hasClass('expanded')) { | ||||
|                                 uiRow.slideUp(); | ||||
|                                 $(this).removeClass('expanded'); | ||||
|                             } else { | ||||
|                                 uiRow.slideDown(); | ||||
|                                 $(this).addClass('expanded'); | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         buildEnvEditRow(uiRow, opt.ui, nameField, valueField); | ||||
|                         nameField.trigger('change'); | ||||
|                     } | ||||
|                 }, | ||||
|                 sortable: ".red-ui-editableList-item-handle", | ||||
|                 removable: false | ||||
|             }); | ||||
|         var parentEnv = {}; | ||||
|         var envList = []; | ||||
|         if (/^subflow:/.test(node.type)) { | ||||
|             var subflowDef = RED.nodes.subflow(node.type.substring(8)); | ||||
|             if (subflowDef.env) { | ||||
|                 subflowDef.env.forEach(function(env) { | ||||
|                     var item = { | ||||
|                         name:env.name, | ||||
|                         parent: { | ||||
|                             type: env.type, | ||||
|                             value: env.value, | ||||
|                             ui: env.ui | ||||
|                         } | ||||
|                     } | ||||
|                     envList.push(item); | ||||
|                     parentEnv[env.name] = item; | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (node.env) { | ||||
|             for (var i = 0; i < node.env.length; i++) { | ||||
|                 var env = node.env[i]; | ||||
|                 if (parentEnv.hasOwnProperty(env.name)) { | ||||
|                     parentEnv[env.name].type = env.type; | ||||
|                     parentEnv[env.name].value = env.value; | ||||
|                 } else { | ||||
|                     envList.push({ | ||||
|                         name: env.name, | ||||
|                         type: env.type, | ||||
|                         value: env.value, | ||||
|                         ui: env.ui | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         envList.forEach(function(env) { | ||||
|             if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') { | ||||
|                 return; | ||||
|             } | ||||
|             if (!isTemplateNode && env.parent) { | ||||
|                 return; | ||||
|             } | ||||
|             envContainer.editableList('addItem', JSON.parse(JSON.stringify(env))); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Create UI edit interface for environment variable | ||||
|      * @param container - container | ||||
|      * @param env - env var definition | ||||
|      * @param nameField - name field of env var | ||||
|      * @param valueField - value field of env var | ||||
|      */ | ||||
|      function buildEnvEditRow(container, ui, nameField, valueField) { | ||||
|          container.addClass("red-ui-editor-subflow-env-ui-row") | ||||
|          var topRow = $('<div></div>').appendTo(container); | ||||
|          $('<div></div>').appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.icon")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.label")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.inputType")).appendTo(topRow); | ||||
|  | ||||
|          var row = $('<div></div>').appendTo(container); | ||||
|          $('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row); | ||||
|          var typeOptions = { | ||||
|              'input': {types:DEFAULT_ENV_TYPE_LIST}, | ||||
|              'select': {opts:[]}, | ||||
|              'spinner': {}, | ||||
|              'cred': {} | ||||
|          }; | ||||
|          if (ui.opts) { | ||||
|              typeOptions[ui.type] = ui.opts; | ||||
|          } else { | ||||
|              // Pick up the default values if not otherwise provided | ||||
|              ui.opts = typeOptions[ui.type]; | ||||
|          } | ||||
|          var iconCell = $('<div></div>').appendTo(row); | ||||
|  | ||||
|          var iconButton = $('<a href="#"></a>').appendTo(iconCell); | ||||
|          iconButton.on("click", function(evt) { | ||||
|              evt.preventDefault(); | ||||
|              var icon = ui.icon || ""; | ||||
|              var iconPath = (icon ? RED.utils.separateIconPath(icon) : {}); | ||||
|              RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) { | ||||
|                  iconButton.empty(); | ||||
|                  var path = newIcon || ""; | ||||
|                  var newPath = RED.utils.separateIconPath(path); | ||||
|                  if (newPath) { | ||||
|                      $('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton); | ||||
|                  } | ||||
|                  ui.icon = path; | ||||
|              }); | ||||
|          }) | ||||
|  | ||||
|          if (ui.icon) { | ||||
|              var newPath = RED.utils.separateIconPath(ui.icon); | ||||
|              $('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton); | ||||
|          } | ||||
|  | ||||
|          var labelCell = $('<div></div>').appendTo(row); | ||||
|  | ||||
|          var label = ui.label && ui.label[currentLocale] || ""; | ||||
|          var labelInput = $('<input type="text">').val(label).appendTo(labelCell); | ||||
|          ui.labelField = labelInput; | ||||
|          labelInput.on('change', function(evt) { | ||||
|              ui.label = ui.label || {}; | ||||
|              var val = $(this).val().trim(); | ||||
|              if (val === "") { | ||||
|                  delete ui.label[currentLocale]; | ||||
|              } else { | ||||
|                  ui.label[currentLocale] = val; | ||||
|              } | ||||
|          }) | ||||
|          var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell); | ||||
|          RED.popover.tooltip(labelIcon,function() { | ||||
|              var langs = Object.keys(ui.label); | ||||
|              var content = $("<div>"); | ||||
|              if (langs.indexOf(currentLocale) === -1) { | ||||
|                  langs.push(currentLocale); | ||||
|                  langs.sort(); | ||||
|              } | ||||
|              langs.forEach(function(l) { | ||||
|                  var row = $('<div>').appendTo(content); | ||||
|                  $('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row); | ||||
|                  $('<span>').text(ui.label[l]||"").appendTo(row); | ||||
|              }); | ||||
|              return content; | ||||
|          }) | ||||
|  | ||||
|          nameField.on('change',function(evt) { | ||||
|             labelInput.attr("placeholder",$(this).val()) | ||||
|         }); | ||||
|  | ||||
|         var inputCell = $('<div></div>').appendTo(row); | ||||
|         var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell); | ||||
|         if (ui.type === "input") { | ||||
|             inputCellInput.val(ui.opts.types.join(",")); | ||||
|         } | ||||
|         var checkbox; | ||||
|         var selectBox; | ||||
|  | ||||
|         inputCellInput.typedInput({ | ||||
|             types: [ | ||||
|                 { | ||||
|                     value:"input", | ||||
|                     label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[ | ||||
|                         {value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"}, | ||||
|                         {value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"}, | ||||
|                         {value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"}, | ||||
|                         {value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"}, | ||||
|                         {value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"}, | ||||
|                         {value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"}, | ||||
|                         {value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"} | ||||
|                     ], | ||||
|                     default: DEFAULT_ENV_TYPE_LIST, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container); | ||||
|  | ||||
|                         var input = $('<div class="placeholder-input">').appendTo(innerContainer); | ||||
|                         $('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input); | ||||
|                         if (value.length) { | ||||
|                             value.forEach(function(v) { | ||||
|                                 if (!/^fa /.test(v.icon)) { | ||||
|                                     $('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input); | ||||
|                                 } else { | ||||
|                                     var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input); | ||||
|                                     $("<i>",{class: v.icon}).appendTo(s); | ||||
|                                 } | ||||
|                             }) | ||||
|                         } else { | ||||
|                             $('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input); | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value: "cred", | ||||
|                     label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({ | ||||
|                             "border-top-right-radius": "4px", | ||||
|                             "border-bottom-right-radius": "4px" | ||||
|                         }).appendTo(container); | ||||
|                         $('<div class="placeholder-input">').html("••••••••").appendTo(innerContainer); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"select", | ||||
|                     label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding","0"); | ||||
|  | ||||
|                         selectBox = $('<select></select>').appendTo(container); | ||||
|                         if (ui.opts && Array.isArray(ui.opts.opts)) { | ||||
|                             ui.opts.opts.forEach(function(o) { | ||||
|                                 var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale); | ||||
|                                 // $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox); | ||||
|                                 $('<option>').val(o.v).text(label).appendTo(selectBox); | ||||
|                             }) | ||||
|                         } | ||||
|                         selectBox.on('change', function(evt) { | ||||
|                             var v = selectBox.val(); | ||||
|                             // var parts = v.split(":"); | ||||
|                             // var t = parts.shift(); | ||||
|                             // v = parts.join(":"); | ||||
|                             // | ||||
|                             // valueField.typedInput("type",'str') | ||||
|                             valueField.typedInput("value",v) | ||||
|                         }); | ||||
|                         selectBox.val(valueField.typedInput("value")); | ||||
|                         // selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value")); | ||||
|                     }, | ||||
|                     expand: { | ||||
|                         icon: "fa-caret-down", | ||||
|                         minWidth: 400, | ||||
|                         content: function(container) { | ||||
|                             var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); | ||||
|                             var optList = $('<ol>').appendTo(content).editableList({ | ||||
|                                 header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"), | ||||
|                                 addItem: function(row,index,itemData) { | ||||
|                                     var labelDiv = $('<div>').appendTo(row); | ||||
|                                     var label = lookupLabel(itemData.l, "", currentLocale); | ||||
|                                     itemData.label = $('<input type="text">').val(label).appendTo(labelDiv); | ||||
|                                     itemData.label.on('keydown', function(evt) { | ||||
|                                         if (evt.keyCode === 13) { | ||||
|                                             itemData.input.focus(); | ||||
|                                             evt.preventDefault(); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv); | ||||
|                                     RED.popover.tooltip(labelIcon,function() { | ||||
|                                         return currentLocale; | ||||
|                                     }) | ||||
|                                     itemData.input = $('<input type="text">').val(itemData.v).appendTo(row); | ||||
|  | ||||
|                                     // Problem using a TI here: | ||||
|                                     //  - this is in a popout panel | ||||
|                                     //  - clicking the expand button in the TI will close the parent edit tray | ||||
|                                     //    and open the type editor. | ||||
|                                     //  - but it leaves the popout panel over the top. | ||||
|                                     //  - there is no way to get back to the popout panel after closing the type editor | ||||
|                                     //.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST}); | ||||
|                                     itemData.input.on('keydown', function(evt) { | ||||
|                                         if (evt.keyCode === 13) { | ||||
|                                             // Enter or Tab | ||||
|                                             var index = optList.editableList('indexOf',itemData); | ||||
|                                             var length = optList.editableList('length'); | ||||
|                                             if (index + 1 === length) { | ||||
|                                                 var newItem = {}; | ||||
|                                                 optList.editableList('addItem',newItem); | ||||
|                                                 setTimeout(function() { | ||||
|                                                     if (newItem.label) { | ||||
|                                                         newItem.label.focus(); | ||||
|                                                     } | ||||
|                                                 },100) | ||||
|                                             } else { | ||||
|                                                 var nextItem = optList.editableList('getItemAt',index+1); | ||||
|                                                 if (nextItem.label) { | ||||
|                                                     nextItem.label.focus() | ||||
|                                                 } | ||||
|                                             } | ||||
|                                             evt.preventDefault(); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                 }, | ||||
|                                 sortable: true, | ||||
|                                 removable: true, | ||||
|                                 height: 160 | ||||
|                             }) | ||||
|                             if (ui.opts.opts.length > 0) { | ||||
|                                 ui.opts.opts.forEach(function(o) { | ||||
|                                     optList.editableList('addItem',$.extend(true,{},o)) | ||||
|                                 }) | ||||
|                             } else { | ||||
|                                 optList.editableList('addItem',{}) | ||||
|                             } | ||||
|                             return { | ||||
|                                 onclose: function() { | ||||
|                                     var items = optList.editableList('items'); | ||||
|                                     var vals = []; | ||||
|                                     items.each(function (i,el) { | ||||
|                                         var data = el.data('data'); | ||||
|                                         var l = data.label.val().trim(); | ||||
|                                         var v = data.input.val(); | ||||
|                                         // var t = data.input.typedInput('type'); | ||||
|                                         // var v = data.input.typedInput('value'); | ||||
|                                         if (l.length > 0) { | ||||
|                                             data.l = data.l || {}; | ||||
|                                             data.l[currentLocale] = l; | ||||
|                                         } | ||||
|                                         data.v = v; | ||||
|  | ||||
|                                         if (l.length > 0 || v.length > 0) { | ||||
|                                             var val = {l:data.l,v:data.v}; | ||||
|                                             // if (t !== 'str') { | ||||
|                                             //     val.t = t; | ||||
|                                             // } | ||||
|                                             vals.push(val); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     ui.opts.opts = vals; | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"checkbox", | ||||
|                     label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         checkbox = $('<input type="checkbox">').appendTo(container); | ||||
|                         checkbox.on('change', function(evt) { | ||||
|                             valueField.typedInput('value',$(this).prop('checked')?"true":"false"); | ||||
|                         }) | ||||
|                         checkbox.prop('checked',valueField.typedInput('value')==="true"); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"spinner", | ||||
|                     label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container); | ||||
|  | ||||
|                         var input = $('<div class="placeholder-input">').appendTo(innerContainer); | ||||
|                         $('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input); | ||||
|  | ||||
|                         var min = ui.opts && ui.opts.min; | ||||
|                         var max = ui.opts && ui.opts.max; | ||||
|                         var label = ""; | ||||
|                         if (min !== undefined && max !== undefined) { | ||||
|                             label = Math.min(min,max)+" - "+Math.max(min,max); | ||||
|                         } else if (min !== undefined) { | ||||
|                             label = "> "+min; | ||||
|                         } else if (max !== undefined) { | ||||
|                             label = "< "+max; | ||||
|                         } | ||||
|                         $('<span>').css("margin-left","15px").text(label).appendTo(input); | ||||
|                     }, | ||||
|                     expand: { | ||||
|                         icon: "fa-caret-down", | ||||
|                         content: function(container) { | ||||
|                             var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); | ||||
|                             content.css("padding","8px 5px") | ||||
|                             var min = ui.opts.min; | ||||
|                             var max = ui.opts.max; | ||||
|                             var minInput = $('<input type="number" style="margin-bottom:0; width:60px">'); | ||||
|                             minInput.val(min); | ||||
|                             var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">'); | ||||
|                             maxInput.val(max); | ||||
|                             $('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content); | ||||
|                             $('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content); | ||||
|                             return { | ||||
|                                 onclose: function() { | ||||
|                                     var min = minInput.val().trim(); | ||||
|                                     var max = maxInput.val().trim(); | ||||
|                                     if (min !== "") { | ||||
|                                         ui.opts.min = parseInt(min); | ||||
|                                     } else { | ||||
|                                         delete ui.opts.min; | ||||
|                                     } | ||||
|                                     if (max !== "") { | ||||
|                                         ui.opts.max = parseInt(max); | ||||
|                                     } else { | ||||
|                                         delete ui.opts.max; | ||||
|                                     } | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"none", | ||||
|                     label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"hide", | ||||
|                     label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false | ||||
|                 } | ||||
|             ], | ||||
|             default: 'none' | ||||
|         }).on("typedinputtypechange", function(evt,type) { | ||||
|             ui.type = $(this).typedInput("type"); | ||||
|             ui.opts = typeOptions[ui.type]; | ||||
|             if (ui.type === 'input') { | ||||
|                 // In the case of 'input' type, the typedInput uses the multiple-option | ||||
|                 // mode. Its value needs to be set to a comma-separately list of the | ||||
|                 // selected options. | ||||
|                 inputCellInput.typedInput('value',ui.opts.types.join(",")) | ||||
|             } else { | ||||
|                 // No other type cares about `value`, but doing this will | ||||
|                 // force a refresh of the label now that `ui.opts` has | ||||
|                 // been updated. | ||||
|                 inputCellInput.typedInput('value',Date.now()) | ||||
|             } | ||||
|  | ||||
|             switch (ui.type) { | ||||
|                 case 'input': | ||||
|                     valueField.typedInput('types',ui.opts.types); | ||||
|                     break; | ||||
|                 case 'select': | ||||
|                     valueField.typedInput('types',['str']); | ||||
|                     break; | ||||
|                 case 'checkbox': | ||||
|                     valueField.typedInput('types',['bool']); | ||||
|                     break; | ||||
|                 case 'spinner': | ||||
|                     valueField.typedInput('types',['num']); | ||||
|                     break; | ||||
|                 case 'cred': | ||||
|                     valueField.typedInput('types',['cred']); | ||||
|                     break; | ||||
|                 default: | ||||
|                     valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST) | ||||
|             } | ||||
|             if (ui.type === 'checkbox') { | ||||
|                 valueField.typedInput('type','bool'); | ||||
|             } else if (ui.type === 'spinner') { | ||||
|                 valueField.typedInput('type','num'); | ||||
|             } | ||||
|             if (ui.type !== 'checkbox') { | ||||
|                 checkbox = null; | ||||
|             } | ||||
|  | ||||
|         }).on("change", function(evt,type) { | ||||
|             if (ui.type === 'input') { | ||||
|                 var types = inputCellInput.typedInput('value'); | ||||
|                 ui.opts.types = (types === "") ? ["str"] : types.split(","); | ||||
|                 valueField.typedInput('types',ui.opts.types); | ||||
|             } | ||||
|         }); | ||||
|         valueField.on("change", function(evt) { | ||||
|             if (checkbox) { | ||||
|                 checkbox.prop('checked',$(this).typedInput('value')==="true") | ||||
|             } | ||||
|         }) | ||||
|         // Set the input to the right type. This will trigger the 'typedinputtypechange' | ||||
|         // event handler (just above ^^) to update the value if needed | ||||
|         inputCellInput.typedInput('type',ui.type) | ||||
|     } | ||||
|  | ||||
|     function setLocale(l, list) { | ||||
|         currentLocale = l; | ||||
|         if (list) { | ||||
|             var items = list.editableList("items"); | ||||
|             items.each(function (i, item) { | ||||
|                 var entry = $(this).data('data'); | ||||
|                 var labelField = entry.ui.labelField; | ||||
|                 labelField.val(lookupLabel(entry.ui.label, "", currentLocale)); | ||||
|                 if (labelField.timeout) { | ||||
|                     clearTimeout(labelField.timeout); | ||||
|                     delete labelField.timeout; | ||||
|                 } | ||||
|                 labelField.addClass("input-updated"); | ||||
|                 labelField.timeout = setTimeout(function() { | ||||
|                     delete labelField.timeout | ||||
|                     labelField.removeClass("input-updated"); | ||||
|                 },3000); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Lookup text for specific locale | ||||
|      * @param labels - dict of labels | ||||
|      * @param defaultLabel - fallback label if not found | ||||
|      * @param locale - target locale | ||||
|      * @returns {string} text for specified locale | ||||
|      */ | ||||
|     function lookupLabel(labels, defaultLabel, locale) { | ||||
|         if (labels) { | ||||
|             if (labels[locale]) { | ||||
|                 return labels[locale]; | ||||
|             } | ||||
|             if (locale) { | ||||
|                 var lang = locale.substring(0, 2); | ||||
|                 if (labels[lang]) { | ||||
|                     return labels[lang]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return defaultLabel; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         create: buildPropertiesList, | ||||
|         setLocale: setLocale, | ||||
|         lookupLabel: lookupLabel, | ||||
|         DEFAULT_ENV_TYPE_LIST: DEFAULT_ENV_TYPE_LIST, | ||||
|         DEFAULT_ENV_TYPE_LIST_INC_CRED: DEFAULT_ENV_TYPE_LIST_INC_CRED | ||||
|     } | ||||
| })(); | ||||
| @@ -50,6 +50,7 @@ | ||||
|         show: function(options) { | ||||
|             var expressionTestCacheId = options.parent||"_"; | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_expression" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
| @@ -63,12 +64,14 @@ | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: "inherit", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if(onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -78,7 +81,8 @@ | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                             $("#red-ui-editor-type-expression-help").text(""); | ||||
|                             onComplete(expressionEditor.getValue()); | ||||
|                             expressionEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -110,6 +114,8 @@ | ||||
|                         id: 'red-ui-editor-type-expression', | ||||
|                         value: "", | ||||
|                         mode:"ace/mode/jsonata", | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true, | ||||
|                         options: { | ||||
|                             enableBasicAutocompletion:true, | ||||
|                             enableSnippets:true, | ||||
| @@ -121,73 +127,75 @@ | ||||
|                     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); | ||||
|                                     } | ||||
|                     //ace only (monaco has jsonata tokeniser) | ||||
|                     if(expressionEditor.type == "ace") { | ||||
|                         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; | ||||
|                                     p = token.index; | ||||
|                                     scopedFunction = token; | ||||
|                                 } 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; | ||||
|                                     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 (p > -1) { | ||||
|                                         var type = rowTokens[p].type; | ||||
|                                         if (next) { | ||||
|                                             if (type === 'keyword') { | ||||
|                                                 scopedFunction = rowTokens[p]; | ||||
|                                                 // console.log("HIT",scopedFunction); | ||||
|                                                 break; | ||||
|                                     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; | ||||
|                                             } | ||||
|                                             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 (type === 'paren.lparen') { | ||||
|                                             depth-=rowTokens[p].value.length; | ||||
|                                         } else if (type === 'paren.rparen') { | ||||
|                                             depth+=rowTokens[p].value.length; | ||||
|                                         if (!scopedFunction) { | ||||
|                                             r--; | ||||
|                                         } | ||||
|                                         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).trigger("change"); | ||||
|                                 } | ||||
|                             } | ||||
|                             expressionEditor.session.removeMarker(currentFunctionMarker); | ||||
|                             if (scopedFunction) { | ||||
|                             //console.log(token,.map(function(t) { return t.type})); | ||||
|                                 funcSelect.val(scopedFunction.value).trigger("change"); | ||||
|                             } | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
|                         }); | ||||
|                     } | ||||
|                     dialogForm.i18n(); | ||||
|                     $("#red-ui-editor-type-expression-func-insert").on("click", function(e) { | ||||
|                         e.preventDefault(); | ||||
| @@ -231,6 +239,8 @@ | ||||
|                     testDataEditor = RED.editor.createEditor({ | ||||
|                         id: 'red-ui-editor-type-expression-test-data', | ||||
|                         value: expressionTestCache[expressionTestCacheId] || '{\n    "payload": "hello world"\n}', | ||||
|                         stateId: false, | ||||
|                         focus: false, | ||||
|                         mode:"ace/mode/json", | ||||
|                         lineNumbers: false | ||||
|                     }); | ||||
| @@ -245,7 +255,7 @@ | ||||
|                         var currentExpression = expressionEditor.getValue(); | ||||
|                         var expr; | ||||
|                         var usesContext = false; | ||||
|                         var legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression); | ||||
|                         var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression); | ||||
|                         $(".red-ui-editor-type-expression-legacy").toggle(legacyMode); | ||||
|                         try { | ||||
|                             expr = jsonata(currentExpression); | ||||
| @@ -300,6 +310,8 @@ | ||||
|                     testResultEditor = RED.editor.createEditor({ | ||||
|                         id: 'red-ui-editor-type-expression-test-result', | ||||
|                         value: "", | ||||
|                         stateId: false, | ||||
|                         focus: false, | ||||
|                         mode:"ace/mode/json", | ||||
|                         lineNumbers: false, | ||||
|                         readOnly: true | ||||
| @@ -343,6 +355,7 @@ | ||||
|                     } | ||||
|                     expressionEditor.destroy(); | ||||
|                     testDataEditor.destroy(); | ||||
|                     testResultEditor.destroy(); | ||||
|                 }, | ||||
|                 show: function() {} | ||||
|             } | ||||
|   | ||||
							
								
								
									
										99
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/iconPicker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/iconPicker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| RED.editor.iconPicker = (function() { | ||||
|     function showIconPicker(container, backgroundColor, iconPath, faOnly, done) { | ||||
|         var picker = $('<div class="red-ui-icon-picker">'); | ||||
|         var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(picker); | ||||
|         searchInput = $('<input type="text">').attr("placeholder",RED._("editor.searchIcons")).appendTo(searchDiv).searchBox({ | ||||
|             delay: 50, | ||||
|             change: function() { | ||||
|                 var searchTerm = $(this).val().trim(); | ||||
|                 if (searchTerm === "") { | ||||
|                     iconList.find(".red-ui-icon-list-module").show(); | ||||
|                     iconList.find(".red-ui-icon-list-icon").show(); | ||||
|                 } else { | ||||
|                     iconList.find(".red-ui-icon-list-module").hide(); | ||||
|                     iconList.find(".red-ui-icon-list-icon").each(function(i,n) { | ||||
|                         if ($(n).data('icon').indexOf(searchTerm) === -1) { | ||||
|                             $(n).hide(); | ||||
|                         } else { | ||||
|                             $(n).show(); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         var row = $('<div>').appendTo(picker); | ||||
|         var iconList = $('<div class="red-ui-icon-list">').appendTo(picker); | ||||
|         var metaRow = $('<div class="red-ui-icon-meta"></div>').appendTo(picker); | ||||
|         var summary = $('<span>').appendTo(metaRow); | ||||
|         var resetButton = $('<button type="button" class="red-ui-button red-ui-button-small">'+RED._("editor.useDefault")+'</button>').appendTo(metaRow).on("click", function(e) { | ||||
|             e.preventDefault(); | ||||
|             iconPanel.hide(); | ||||
|             done(null); | ||||
|         }); | ||||
|         if (!backgroundColor && faOnly) { | ||||
|             iconList.addClass("red-ui-icon-list-dark"); | ||||
|         } | ||||
|         setTimeout(function() { | ||||
|             var iconSets = RED.nodes.getIconSets(); | ||||
|             Object.keys(iconSets).forEach(function(moduleName) { | ||||
|                 if (faOnly && (moduleName !== "font-awesome")) { | ||||
|                     return; | ||||
|                 } | ||||
|                 var icons = iconSets[moduleName]; | ||||
|                 if (icons.length > 0) { | ||||
|                     // selectIconModule.append($("<option></option>").val(moduleName).text(moduleName)); | ||||
|                     var header = $('<div class="red-ui-icon-list-module"></div>').text(moduleName).appendTo(iconList); | ||||
|                     $('<i class="fa fa-cube"></i>').prependTo(header); | ||||
|                     icons.forEach(function(icon) { | ||||
|                         var iconDiv = $('<div>',{class:"red-ui-icon-list-icon"}).appendTo(iconList); | ||||
|                         var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconDiv); | ||||
|                         var icon_url = RED.settings.apiRootUrl+"icons/"+moduleName+"/"+icon; | ||||
|                         iconDiv.data('icon',icon_url); | ||||
|                         if (backgroundColor) { | ||||
|                             nodeDiv.css({ | ||||
|                                 'backgroundColor': backgroundColor | ||||
|                             }); | ||||
|                             var borderColor = RED.utils.getDarkerColor(backgroundColor); | ||||
|                             if (borderColor !== backgroundColor) { | ||||
|                                 nodeDiv.css('border-color',borderColor) | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|                         RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|  | ||||
|                         if (iconPath.module === moduleName && iconPath.file === icon) { | ||||
|                             iconDiv.addClass("selected"); | ||||
|                         } | ||||
|                         iconDiv.on("mouseover", function() { | ||||
|                             summary.text(icon); | ||||
|                         }) | ||||
|                         iconDiv.on("mouseout", function() { | ||||
|                             summary.html(" "); | ||||
|                         }) | ||||
|                         iconDiv.on("click", function() { | ||||
|                             iconPanel.hide(); | ||||
|                             done(moduleName+"/"+icon); | ||||
|                         }) | ||||
|                     }) | ||||
|                 } | ||||
|             }); | ||||
|             setTimeout(function() { | ||||
|                 spinner.remove(); | ||||
|             },50); | ||||
|         },300); | ||||
|         var spinner = RED.utils.addSpinnerOverlay(iconList,true); | ||||
|         var iconPanel = RED.popover.panel(picker); | ||||
|         iconPanel.show({ | ||||
|             target: container | ||||
|         }) | ||||
|  | ||||
|  | ||||
|         picker.slideDown(100); | ||||
|         searchInput.trigger("focus"); | ||||
|     } | ||||
|     return { | ||||
|         show: showIconPicker | ||||
|     } | ||||
| })(); | ||||
| @@ -21,6 +21,7 @@ | ||||
|     var definition = { | ||||
|         show: function(options) { | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_js" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
| @@ -28,16 +29,16 @@ | ||||
|             } | ||||
|             RED.view.state(RED.state.EDITING); | ||||
|             var expressionEditor; | ||||
|             var changeTimer; | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: options.width||"inherit", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if (onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -46,7 +47,8 @@ | ||||
|                         text: RED._("common.label.done"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                             onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition()); | ||||
|                             expressionEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(expressionEditor.getValue(), expressionEditor.getCursorPosition(), expressionEditor); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -62,11 +64,12 @@ | ||||
|                     expressionEditor.resize(); | ||||
|                 }, | ||||
|                 open: function(tray) { | ||||
|                     var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|                     expressionEditor = RED.editor.createEditor({ | ||||
|                         id: 'node-input-js', | ||||
|                         mode: options.mode || 'ace/mode/javascript', | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true, | ||||
|                         value: value, | ||||
|                         globals: { | ||||
|                             msg:true, | ||||
| @@ -81,21 +84,19 @@ | ||||
|                             clearTimeout: true, | ||||
|                             setInterval: true, | ||||
|                             clearInterval: true | ||||
|                         } | ||||
|                         }, | ||||
|                         extraLibs: options.extraLibs | ||||
|                     }); | ||||
|                     if (options.cursor) { | ||||
|                     if (options.cursor && !expressionEditor._initState) { | ||||
|                         expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); | ||||
|                     } | ||||
|                     dialogForm.i18n(); | ||||
|                     setTimeout(function() { | ||||
|                         expressionEditor.focus(); | ||||
|                     },300); | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     expressionEditor.destroy(); | ||||
|                     if (options.onclose) { | ||||
|                         options.onclose(); | ||||
|                     } | ||||
|                     expressionEditor.destroy(); | ||||
|                 }, | ||||
|                 show: function() {} | ||||
|             } | ||||
|   | ||||
| @@ -21,7 +21,9 @@ | ||||
|         '<ul id="red-ui-editor-type-json-tabs"></ul>'+ | ||||
|         '<div id="red-ui-editor-type-json-tab-raw" class="red-ui-editor-type-json-tab-content hide">'+ | ||||
|             '<div class="form-row" style="margin-bottom: 3px; text-align: right;">'+ | ||||
|                 '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+ | ||||
|                 '<span class="button-group">'+ | ||||
|                     '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+ | ||||
|                 '<span class="button-group">'+ | ||||
|             '</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>'+ | ||||
| @@ -34,7 +36,7 @@ | ||||
|  | ||||
|     var activeTab; | ||||
|  | ||||
|     function insertNewItem(parent,index,copyIndex) { | ||||
|     function insertNewItem(parent,index,copyIndex,readOnly) { | ||||
|         var newValue = ""; | ||||
|  | ||||
|         if (parent.children.length > 0) { | ||||
| @@ -60,26 +62,26 @@ | ||||
|                 newKey = keyRoot+"-"+(keySuffix++); | ||||
|             } | ||||
|         } | ||||
|         var newItem = handleItem(newKey,newValue,parent.depth+1,parent); | ||||
|         var newItem = handleItem(newKey,newValue,parent.depth+1,parent,readOnly); | ||||
|         parent.treeList.insertChildAt(newItem, index, true); | ||||
|         parent.treeList.expand(); | ||||
|     } | ||||
|     function showObjectMenu(button,item) { | ||||
|     function showObjectMenu(button,item,readOnly) { | ||||
|         var elementPos = button.offset(); | ||||
|         var options = []; | ||||
|         if (item.parent) { | ||||
|             options.push({id:"red-ui-editor-type-json-menu-insert-above", icon:"fa fa-toggle-up", label:RED._('jsonEditor.insertAbove'),onselect:function(){ | ||||
|                 var index = item.parent.children.indexOf(item); | ||||
|                 insertNewItem(item.parent,index,index); | ||||
|                 insertNewItem(item.parent,index,index,readOnly); | ||||
|             }}); | ||||
|             options.push({id:"red-ui-editor-type-json-menu-insert-below", icon:"fa fa-toggle-down", label:RED._('jsonEditor.insertBelow'),onselect:function(){ | ||||
|                 var index = item.parent.children.indexOf(item)+1; | ||||
|                 insertNewItem(item.parent,index,index-1); | ||||
|                 insertNewItem(item.parent,index,index-1,readOnly); | ||||
|             }}); | ||||
|         } | ||||
|         if (item.type === 'array' || item.type === 'object') { | ||||
|             options.push({id:"red-ui-editor-type-json-menu-add-child", icon:"fa fa-plus", label:RED._('jsonEditor.addItem'),onselect:function(){ | ||||
|                 insertNewItem(item,item.children.length,item.children.length-1); | ||||
|                 insertNewItem(item,item.children.length,item.children.length-1,readOnly); | ||||
|             }}); | ||||
|         } | ||||
|         if (item.parent) { | ||||
| @@ -121,7 +123,7 @@ | ||||
|                         newKey = keyRoot+"-"+(keySuffix++); | ||||
|                     } | ||||
|                 } | ||||
|                 var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent); | ||||
|                 var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent,readOnly); | ||||
|                 var index = item.parent.children.indexOf(item)+1; | ||||
|  | ||||
|                 item.parent.treeList.insertChildAt(newItem, index, true); | ||||
| @@ -171,24 +173,24 @@ | ||||
|         menuOptionMenu.show(); | ||||
|     } | ||||
|  | ||||
|     function parseObject(obj,depth,parent) { | ||||
|     function parseObject(obj,depth,parent,readOnly) { | ||||
|         var result = []; | ||||
|         for (var prop in obj) { | ||||
|             if (obj.hasOwnProperty(prop)) { | ||||
|                 result.push(handleItem(prop,obj[prop],depth,parent)); | ||||
|                 result.push(handleItem(prop,obj[prop],depth,parent,readOnly)); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     function parseArray(obj,depth,parent) { | ||||
|     function parseArray(obj,depth,parent,readOnly) { | ||||
|         var result = []; | ||||
|         var l = obj.length; | ||||
|         for (var i=0;i<l;i++) { | ||||
|             result.push(handleItem(i,obj[i],depth,parent)); | ||||
|             result.push(handleItem(i,obj[i],depth,parent,readOnly)); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     function handleItem(key,val,depth,parent) { | ||||
|     function handleItem(key,val,depth,parent,readOnly) { | ||||
|         var item = {depth:depth, type: typeof val}; | ||||
|         var container = $('<span class="red-ui-editor-type-json-editor-label">'); | ||||
|         if (key != null) { | ||||
| @@ -204,11 +206,14 @@ | ||||
|             if (parent && parent.type === "array") { | ||||
|                 keyLabel.addClass("red-ui-editor-type-json-editor-label-array-key") | ||||
|             } | ||||
|  | ||||
|             if(readOnly) { | ||||
|                 keyLabel.addClass("readonly") | ||||
|             } | ||||
|             keyLabel.on("click", function(evt) { | ||||
|                 if (item.parent.type === 'array') { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (readOnly) { return; } | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 var w = Math.max(150,keyLabel.width()); | ||||
| @@ -253,10 +258,10 @@ | ||||
|             item.expanded = depth < 2; | ||||
|             item.type = "array"; | ||||
|             item.deferBuild = depth >= 2; | ||||
|             item.children = parseArray(val,depth+1,item); | ||||
|             item.children = parseArray(val,depth+1,item,readOnly); | ||||
|         } else if (val !== null && item.type === "object") { | ||||
|             item.expanded = depth < 2; | ||||
|             item.children = parseObject(val,depth+1,item); | ||||
|             item.children = parseObject(val,depth+1,item,readOnly); | ||||
|             item.deferBuild = depth >= 2; | ||||
|         } else { | ||||
|             item.value = val; | ||||
| @@ -287,7 +292,11 @@ | ||||
|         // | ||||
|         var orphanedChildren; | ||||
|         var valueLabel = $('<span class="red-ui-editor-type-json-editor-label-value">').addClass(valClass).text(valValue).appendTo(container); | ||||
|         if (readOnly) { | ||||
|             valueLabel.addClass("readonly") | ||||
|         } | ||||
|         valueLabel.on("click", function(evt) { | ||||
|             if (readOnly) { return; } | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             if (valType === 'str') { | ||||
| @@ -302,8 +311,8 @@ | ||||
|                 types:[ | ||||
|                     'str','num','bool', | ||||
|                     {value:"null",label:RED._("common.type.null"),hasValue:false}, | ||||
|                     {value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.png"}, | ||||
|                     {value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.png"} | ||||
|                     {value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.svg"}, | ||||
|                     {value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.svg"} | ||||
|                 ], | ||||
|                 default: valType | ||||
|             }); | ||||
| @@ -395,17 +404,19 @@ | ||||
|             valueLabel.hide(); | ||||
|         }) | ||||
|         item.gutter = $('<span class="red-ui-editor-type-json-editor-item-gutter"></span>'); | ||||
|  | ||||
|         if (parent) {//red-ui-editor-type-json-editor-item-handle | ||||
|             $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter); | ||||
|         } else { | ||||
|             $('<span></span>').appendTo(item.gutter); | ||||
|         if(!readOnly) { | ||||
|             if (parent) { | ||||
|                 $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter); | ||||
|             } else { | ||||
|                 $('<span></span>').appendTo(item.gutter); | ||||
|             } | ||||
|             $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 showObjectMenu($(this), item, readOnly); | ||||
|             }); | ||||
|         } | ||||
|         $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             showObjectMenu($(this), item); | ||||
|         }); | ||||
|  | ||||
|         item.element = container; | ||||
|         return item; | ||||
|     } | ||||
| @@ -434,6 +445,7 @@ | ||||
|     var definition = { | ||||
|         show: function(options) { | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_json" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
| @@ -455,15 +467,16 @@ | ||||
|                 } | ||||
|             } | ||||
|             var rootNode; | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: options.width||700, | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if (onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -485,7 +498,8 @@ | ||||
|                             } else if (activeTab === "json-raw") { | ||||
|                                 result = expressionEditor.getValue(); | ||||
|                             } | ||||
|                             if (onComplete) { onComplete(result) } | ||||
|                             expressionEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(result,null,expressionEditor) } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -498,7 +512,25 @@ | ||||
|                 open: function(tray) { | ||||
|                     var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|  | ||||
|                     var toolbarButtons = options.toolbarButtons || []; | ||||
|                     if (toolbarButtons.length) { | ||||
|                         toolbarButtons.forEach(function (button) { | ||||
|                             var element = $('<button type="button" class="red-ui-button red-ui-button-small"> </button>') | ||||
|                                 .insertBefore("#node-input-json-reformat") | ||||
|                                 .on("click", function (evt) { | ||||
|                                     evt.preventDefault(); | ||||
|                                     if (button.click !== undefined) { | ||||
|                                         button.click.call(element, evt); | ||||
|                                     } | ||||
|                                 }); | ||||
|                             if (button.id) { element.attr("id", button.id); } | ||||
|                             if (button.title) { element.attr("title", button.title); } | ||||
|                             if (button.icon) { element.append($("<i></i>").attr("class", button.icon)); } | ||||
|                             if (button.label || button.text) { | ||||
|                                 element.append($("<span></span>").text(" " + (button.label || button.text))); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                     var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"}); | ||||
|                     var filterDepth = Infinity; | ||||
|                     var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({ | ||||
| @@ -528,13 +560,15 @@ | ||||
|                         }) | ||||
|                     }); | ||||
|  | ||||
|  | ||||
|                     expressionEditor = RED.editor.createEditor({ | ||||
|                         id: 'node-input-json', | ||||
|                         value: "", | ||||
|                         mode:"ace/mode/json" | ||||
|                         value: value||"", | ||||
|                         mode:"ace/mode/json", | ||||
|                         readOnly: !!options.readOnly, | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true | ||||
|                     }); | ||||
|                     expressionEditor.getSession().setValue(value||"",-1); | ||||
|  | ||||
|                     if (options.requireValid) { | ||||
|                         expressionEditor.getSession().on('change', function() { | ||||
|                             clearTimeout(changeTimer); | ||||
| @@ -571,7 +605,7 @@ | ||||
|                                     var raw = expressionEditor.getValue().trim() ||"{}"; | ||||
|                                     try { | ||||
|                                         var parsed = JSON.parse(raw); | ||||
|                                         rootNode = handleItem(null,parsed,0,null); | ||||
|                                         rootNode = handleItem(null,parsed,0,null,options.readOnly); | ||||
|                                         rootNode.class = "red-ui-editor-type-json-root-node" | ||||
|                                         list.treeList('data',[rootNode]); | ||||
|                                     } catch(err) { | ||||
| @@ -589,23 +623,21 @@ | ||||
|  | ||||
|                     tabs.addTab({ | ||||
|                         id: 'json-raw', | ||||
|                         label: RED._('jsonEditor.rawMode'), | ||||
|                         label: options.readOnly ? RED._('jsonEditor.rawMode-readonly') : RED._('jsonEditor.rawMode'), | ||||
|                         content: $("#red-ui-editor-type-json-tab-raw") | ||||
|                     }); | ||||
|                     tabs.addTab({ | ||||
|                         id: 'json-ui', | ||||
|                         label: RED._('jsonEditor.uiMode'), | ||||
|                         label: options.readOnly ? RED._('jsonEditor.uiMode-readonly') : RED._('jsonEditor.uiMode'), | ||||
|                         content: $("#red-ui-editor-type-json-tab-ui") | ||||
|                     }); | ||||
|                     finishedBuild = true; | ||||
|  | ||||
|  | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     // expressionEditor.destroy(); | ||||
|                     if (options.onclose) { | ||||
|                         options.onclose(); | ||||
|                     } | ||||
|                     expressionEditor.destroy(); | ||||
|                 }, | ||||
|                 show: function() {} | ||||
|             } | ||||
|   | ||||
| @@ -54,24 +54,26 @@ | ||||
|     var definition = { | ||||
|         show: function(options) { | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_markdown" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
|                 $(template).appendTo("#red-ui-editor-node-configs"); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             RED.view.state(RED.state.EDITING); | ||||
|             var expressionEditor; | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: options.width||Infinity, | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if (onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -80,7 +82,8 @@ | ||||
|                         text: RED._("common.label.done"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                             onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition()); | ||||
|                             expressionEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(), expressionEditor); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -99,6 +102,8 @@ | ||||
|                     expressionEditor = RED.editor.createEditor({ | ||||
|                         id: 'red-ui-editor-type-markdown', | ||||
|                         value: value, | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true, | ||||
|                         mode:"ace/mode/markdown", | ||||
|                         expandable: false | ||||
|                     }); | ||||
| @@ -143,17 +148,17 @@ | ||||
|                     }); | ||||
|                     RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview")); | ||||
|  | ||||
|                     if (options.cursor) { | ||||
|                     if (options.cursor && !expressionEditor._initState) { | ||||
|                         expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); | ||||
|                     } | ||||
|  | ||||
|                     dialogForm.i18n(); | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     expressionEditor.destroy(); | ||||
|                     if (options.onclose) { | ||||
|                         options.onclose(); | ||||
|                     } | ||||
|                     expressionEditor.destroy(); | ||||
|                 }, | ||||
|                 show: function() {} | ||||
|             } | ||||
| @@ -168,7 +173,7 @@ | ||||
|                 'b': { before:"**", after: "**", tooltip: RED._("markdownEditor.bold")}, | ||||
|                 'i': { before:"_", after: "_", tooltip: RED._("markdownEditor.italic")}, | ||||
|                 'code': { before:"`", after: "`", tooltip: RED._("markdownEditor.code")}, | ||||
|                 'ol': { before:" * ", newline: true, tooltip: RED._("markdownEditor.ordered-list")}, | ||||
|                 'ol': { before:" 1. ", newline: true, tooltip: RED._("markdownEditor.ordered-list")}, | ||||
|                 'ul': { before:" - ", newline: true, tooltip: RED._("markdownEditor.unordered-list")}, | ||||
|                 'bq': { before:"> ", newline: true, tooltip: RED._("markdownEditor.quote")}, | ||||
|                 'link': { before:"[", after: "]()", tooltip: RED._("markdownEditor.link")}, | ||||
|   | ||||
							
								
								
									
										530
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										530
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,530 @@ | ||||
| ;(function() { | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-appearance", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.appearance"), | ||||
|             name: RED._("editor-tab.appearance"), | ||||
|             iconClass: "fa fa-object-group", | ||||
|             create: function(container) { | ||||
|                 this.content = container; | ||||
|                 buildAppearanceForm(this.content,node); | ||||
|  | ||||
|                 if (node.type === 'subflow') { | ||||
|                     this.defaultIcon = "node-red/subflow.svg"; | ||||
|                 } else { | ||||
|                     var iconPath = RED.utils.getDefaultNodeIcon(node._def,node); | ||||
|                     this.defaultIcon = iconPath.module+"/"+iconPath.file; | ||||
|                     if (node.icon && node.icon !== this.defaultIcon) { | ||||
|                         this.isDefaultIcon = false; | ||||
|                     } else { | ||||
|                         this.isDefaultIcon = true; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|  | ||||
|             }, | ||||
|             close: function() { | ||||
|  | ||||
|             }, | ||||
|             show: function() { | ||||
|                 refreshLabelForm(this.content, node); | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 if (updateLabels(node, editState.changes, editState.outputMap)) { | ||||
|                     editState.changed = true; | ||||
|                 } | ||||
|                 if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) { | ||||
|                     var icon = $("#red-ui-editor-node-icon").val()||""; | ||||
|                     if (!this.isDefaultIcon) { | ||||
|                         if ((icon !== node.icon) && | ||||
|                             (icon !== "")) { | ||||
|                             editState.changes.icon = node.icon; | ||||
|                             node.icon = icon; | ||||
|                             editState.changed = true; | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (icon !== "" && icon !== this.defaultIcon) { | ||||
|                             editState.changes.icon = node.icon; | ||||
|                             node.icon = icon; | ||||
|                             editState.changed = true; | ||||
|                         } else { | ||||
|                             var iconPath = RED.utils.getDefaultNodeIcon(node._def, node); | ||||
|                             var currentDefaultIcon = iconPath.module+"/"+iconPath.file; | ||||
|                             if (this.defaultIcon !== currentDefaultIcon) { | ||||
|                                 editState.changes.icon = node.icon; | ||||
|                                 node.icon = currentDefaultIcon; | ||||
|                                 editState.changed = true; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (node.type === "subflow") { | ||||
|                     var newCategory = $("#subflow-appearance-input-category").val().trim(); | ||||
|                     if (newCategory === "_custom_") { | ||||
|                         newCategory = $("#subflow-appearance-input-custom-category").val().trim(); | ||||
|                         if (newCategory === "") { | ||||
|                             newCategory = node.category; | ||||
|                         } | ||||
|                     } | ||||
|                     if (newCategory === 'subflows') { | ||||
|                         newCategory = ''; | ||||
|                     } | ||||
|                     if (newCategory != node.category) { | ||||
|                         editState.changes['category'] = node.category; | ||||
|                         node.category = newCategory; | ||||
|                         editState.changed = true; | ||||
|                     } | ||||
|  | ||||
|                     var oldColor = node.color; | ||||
|                     var newColor =  $("#red-ui-editor-node-color").val(); | ||||
|                     if (oldColor !== newColor) { | ||||
|                         editState.changes.color = node.color; | ||||
|                         node.color = newColor; | ||||
|                         editState.changed = true; | ||||
|                         RED.utils.clearNodeColorCache(); | ||||
|                         if (node.type === "subflow") { | ||||
|                             var nodeDefinition = RED.nodes.getType( | ||||
|                                 "subflow:" + node.id | ||||
|                             ); | ||||
|                             nodeDefinition["color"] = newColor; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|  | ||||
|  | ||||
|                 } | ||||
|                 var showLabel = node._def.hasOwnProperty("showLabel")?node._def.showLabel:true; | ||||
|  | ||||
|                 if (!$("#node-input-show-label").prop('checked')) { | ||||
|                     // Not checked - hide label | ||||
|  | ||||
|                     if (showLabel) { | ||||
|                         // Default to show label | ||||
|                         if (node.l !== false) { | ||||
|                             editState.changes.l = node.l; | ||||
|                             editState.changed = true; | ||||
|                         } | ||||
|                         node.l = false; | ||||
|                     } else { | ||||
|                         // Node has showLabel:false (eg link nodes) | ||||
|                         if (node.hasOwnProperty('l') && node.l) { | ||||
|                             editState.changes.l = node.l; | ||||
|                             editState.changed = true; | ||||
|                         } | ||||
|                         delete node.l; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // Checked - show label | ||||
|                     if (showLabel) { | ||||
|                         // Default to show label | ||||
|                         if (node.hasOwnProperty('l') && !node.l) { | ||||
|                             editState.changes.l = node.l; | ||||
|                             editState.changed = true; | ||||
|                         } | ||||
|                         delete node.l; | ||||
|                     } else { | ||||
|                         if (!node.l) { | ||||
|                             editState.changes.l = node.l; | ||||
|                             editState.changed = true; | ||||
|                         } | ||||
|                         node.l = true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|     function buildAppearanceForm(container,node) { | ||||
|         var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container); | ||||
|  | ||||
|         var i,row; | ||||
|  | ||||
|         if (node.type === "subflow") { | ||||
|             var categoryRow = $("<div/>", { | ||||
|                 class: "form-row" | ||||
|             }).appendTo(dialogForm); | ||||
|             $("<label/>", { | ||||
|                 for: "subflow-appearance-input-category", | ||||
|                 "data-i18n": "editor:subflow.category" | ||||
|             }).appendTo(categoryRow); | ||||
|             var categorySelector = $("<select/>", { | ||||
|                 id: "subflow-appearance-input-category" | ||||
|             }).css({ | ||||
|                 width: "250px" | ||||
|             }).appendTo(categoryRow); | ||||
|             $("<input/>", { | ||||
|                 type: "text", | ||||
|                 id: "subflow-appearance-input-custom-category" | ||||
|             }).css({ | ||||
|                 display: "none", | ||||
|                 "margin-left": "10px", | ||||
|                 width: "calc(100% - 250px)" | ||||
|             }).appendTo(categoryRow); | ||||
|  | ||||
|             var categories = RED.palette.getCategories(); | ||||
|             categories.sort(function(A,B) { | ||||
|                 return A.label.localeCompare(B.label); | ||||
|             }); | ||||
|             categories.forEach(function(cat) { | ||||
|                 categorySelector.append($("<option/>").val(cat.id).text(cat.label)); | ||||
|             }); | ||||
|             categorySelector.append($("<option/>").attr('disabled',true).text("---")); | ||||
|             categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory"))); | ||||
|  | ||||
|             $("#subflow-appearance-input-category").on("change", function() { | ||||
|                 var val = $(this).val(); | ||||
|                 if (val === "_custom_") { | ||||
|                     $("#subflow-appearance-input-category").width(120); | ||||
|                     $("#subflow-appearance-input-custom-category").show(); | ||||
|                 } else { | ||||
|                     $("#subflow-appearance-input-category").width(250); | ||||
|                     $("#subflow-appearance-input-custom-category").hide(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             $("#subflow-appearance-input-category").val(node.category||"subflows"); | ||||
|             var userCount = 0; | ||||
|             var subflowType = "subflow:"+node.id; | ||||
|  | ||||
|             // RED.nodes.eachNode(function(n) { | ||||
|             //     if (n.type === subflowType) { | ||||
|             //         userCount++; | ||||
|             //     } | ||||
|             // }); | ||||
|             $("#red-ui-editor-subflow-user-count") | ||||
|                 .text(RED._("subflow.subflowInstances", {count:node.instances.length})).show(); | ||||
|         } | ||||
|  | ||||
|         $('<div class="form-row">'+ | ||||
|             '<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+ | ||||
|             '<span style="margin-right: 2px;"/>'+ | ||||
|             '<input type="checkbox" id="node-input-show-label"/>'+ | ||||
|         '</div>').appendTo(dialogForm); | ||||
|  | ||||
|         $("#node-input-show-label").toggleButton({ | ||||
|             enabledLabel: RED._("editor.show"), | ||||
|             disabledLabel: RED._("editor.hide") | ||||
|         }); | ||||
|  | ||||
|         if (!node.hasOwnProperty("l")) { | ||||
|             // Show label unless def.showLabel set to false | ||||
|             node.l =  node._def.hasOwnProperty("showLabel")?node._def.showLabel:true; | ||||
|         } | ||||
|         $("#node-input-show-label").prop("checked",node.l).trigger("change"); | ||||
|  | ||||
|         if (node.type === "subflow") { | ||||
|             // subflow template can select its color | ||||
|             var color = node.color ? node.color : "#DDAA99"; | ||||
|             var colorRow = $("<div/>", { | ||||
|                 class: "form-row" | ||||
|             }).appendTo(dialogForm); | ||||
|             $("<label/>").text(RED._("editor.color")).appendTo(colorRow); | ||||
|  | ||||
|             var recommendedColors = [ | ||||
|                 "#DDAA99", | ||||
|                 "#3FADB5", "#87A980", "#A6BBCF", | ||||
|                 "#AAAA66", "#C0C0C0", "#C0DEED", | ||||
|                 "#C7E9C0", "#D7D7A0", "#D8BFD8", | ||||
|                 "#DAC4B4", "#DEB887", "#DEBD5C", | ||||
|                 "#E2D96E", "#E6E0F8", "#E7E7AE", | ||||
|                 "#E9967A", "#F3B567", "#FDD0A2", | ||||
|                 "#FDF0C2", "#FFAAAA", "#FFCC66", | ||||
|                 "#FFF0F0", "#FFFFFF" | ||||
|             ]; | ||||
|  | ||||
|             RED.editor.colorPicker.create({ | ||||
|                 id: "red-ui-editor-node-color", | ||||
|                 value: color, | ||||
|                 palette: recommendedColors, | ||||
|                 sortPalette: function (a, b) {return a.l - b.l;} | ||||
|             }).appendTo(colorRow); | ||||
|  | ||||
|             $("#red-ui-editor-node-color").on('change', function(ev) { | ||||
|                 // Horribly out of scope... | ||||
|                 var colour = $(this).val(); | ||||
|                 nodeDiv.css('backgroundColor',colour); | ||||
|                 var borderColor = RED.utils.getDarkerColor(colour); | ||||
|                 if (borderColor !== colour) { | ||||
|                     nodeDiv.css('border-color',borderColor); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // If a node has icon property in defaults, the icon of the node cannot be modified. (e.g, ui_button node in dashboard) | ||||
|         if ((!node._def.defaults || !node._def.defaults.hasOwnProperty("icon"))) { | ||||
|             var iconRow = $('<div class="form-row"></div>').appendTo(dialogForm); | ||||
|             $('<label data-i18n="editor.settingIcon">').appendTo(iconRow); | ||||
|  | ||||
|             var iconButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(iconRow); | ||||
|             $('<i class="fa fa-caret-down"></i>').appendTo(iconButton); | ||||
|             var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconButton); | ||||
|             var colour = RED.utils.getNodeColor(node.type, node._def); | ||||
|             var icon_url = RED.utils.getNodeIcon(node._def,node); | ||||
|             nodeDiv.css('backgroundColor',colour); | ||||
|             var borderColor = RED.utils.getDarkerColor(colour); | ||||
|             if (borderColor !== colour) { | ||||
|                 nodeDiv.css('border-color',borderColor); | ||||
|             } | ||||
|  | ||||
|             var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|             RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|  | ||||
|             iconButton.on("click", function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 var iconPath; | ||||
|                 var icon = $("#red-ui-editor-node-icon").val()||""; | ||||
|                 if (icon) { | ||||
|                     iconPath = RED.utils.separateIconPath(icon); | ||||
|                 } else { | ||||
|                     iconPath = RED.utils.getDefaultNodeIcon(node._def, node); | ||||
|                 } | ||||
|                 var backgroundColor = RED.utils.getNodeColor(node.type, node._def); | ||||
|                 if (node.type === "subflow") { | ||||
|                     backgroundColor = $("#red-ui-editor-node-color").val(); | ||||
|                 } | ||||
|                 RED.editor.iconPicker.show(iconButton,backgroundColor,iconPath,false,function(newIcon) { | ||||
|                     $("#red-ui-editor-node-icon").val(newIcon||""); | ||||
|                     var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon}); | ||||
|                     RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             RED.popover.tooltip(iconButton, function() { | ||||
|                 return $("#red-ui-editor-node-icon").val() || RED._("editor.default"); | ||||
|             }); | ||||
|             $('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $('<div class="form-row"><span data-i18n="editor.portLabels"></span></div>').appendTo(dialogForm); | ||||
|  | ||||
|         var inputCount = node.inputs || node._def.inputs || 0; | ||||
|         var outputCount = node.outputs || node._def.outputs || 0; | ||||
|         if (node.type === 'subflow') { | ||||
|             inputCount = node.in.length; | ||||
|             outputCount = node.out.length; | ||||
|         } | ||||
|  | ||||
|         var inputLabels = node.inputLabels || []; | ||||
|         var outputLabels = node.outputLabels || []; | ||||
|  | ||||
|         var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel"); | ||||
|         var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel"); | ||||
|  | ||||
|         $('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelInputs"></span><div id="red-ui-editor-node-label-form-inputs"></div></div>').appendTo(dialogForm); | ||||
|         var inputsDiv = $("#red-ui-editor-node-label-form-inputs"); | ||||
|         if (inputCount > 0) { | ||||
|             for (i=0;i<inputCount;i++) { | ||||
|                 buildLabelRow("input",i,inputLabels[i],inputPlaceholder).appendTo(inputsDiv); | ||||
|             } | ||||
|         } else { | ||||
|             buildLabelRow().appendTo(inputsDiv); | ||||
|         } | ||||
|         $('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelOutputs"></span><div id="red-ui-editor-node-label-form-outputs"></div></div>').appendTo(dialogForm); | ||||
|         var outputsDiv = $("#red-ui-editor-node-label-form-outputs"); | ||||
|         if (outputCount > 0) { | ||||
|             for (i=0;i<outputCount;i++) { | ||||
|                 buildLabelRow("output",i,outputLabels[i],outputPlaceholder).appendTo(outputsDiv); | ||||
|             } | ||||
|         } else { | ||||
|             buildLabelRow().appendTo(outputsDiv); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function refreshLabelForm(container,node) { | ||||
|  | ||||
|         var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel"); | ||||
|         var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel"); | ||||
|  | ||||
|         var inputsDiv = $("#red-ui-editor-node-label-form-inputs"); | ||||
|         var outputsDiv = $("#red-ui-editor-node-label-form-outputs"); | ||||
|  | ||||
|         var inputCount; | ||||
|         var formInputs = $("#node-input-inputs").val(); | ||||
|         if (formInputs === undefined) { | ||||
|             if (node.type === 'subflow') { | ||||
|                 inputCount = node.in.length; | ||||
|             } else { | ||||
|                 inputCount = node.inputs || node._def.inputs || 0; | ||||
|             } | ||||
|         } else { | ||||
|             inputCount = Math.min(1,Math.max(0,parseInt(formInputs))); | ||||
|             if (isNaN(inputCount)) { | ||||
|                 inputCount = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var children = inputsDiv.children(); | ||||
|         var childCount = children.length; | ||||
|         if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) { | ||||
|             childCount--; | ||||
|         } | ||||
|  | ||||
|         if (childCount < inputCount) { | ||||
|             if (childCount === 0) { | ||||
|                 // remove the 'none' placeholder | ||||
|                 $(children[0]).remove(); | ||||
|             } | ||||
|             for (i = childCount;i<inputCount;i++) { | ||||
|                 buildLabelRow("input",i,"",inputPlaceholder).appendTo(inputsDiv); | ||||
|             } | ||||
|         } else if (childCount > inputCount) { | ||||
|             for (i=inputCount;i<childCount;i++) { | ||||
|                 $(children[i]).remove(); | ||||
|             } | ||||
|             if (inputCount === 0) { | ||||
|                 buildLabelRow().appendTo(inputsDiv); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var outputCount; | ||||
|         var i; | ||||
|         var formOutputs = $("#node-input-outputs").val(); | ||||
|  | ||||
|         if (formOutputs === undefined) { | ||||
|             if (node.type === 'subflow') { | ||||
|                 outputCount = node.out.length; | ||||
|             } else { | ||||
|                 inputCount = node.outputs || node._def.outputs || 0; | ||||
|             } | ||||
|         } else if (isNaN(formOutputs)) { | ||||
|             var outputMap = JSON.parse(formOutputs); | ||||
|             var keys = Object.keys(outputMap); | ||||
|             children = outputsDiv.children(); | ||||
|             childCount = children.length; | ||||
|             if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) { | ||||
|                 childCount--; | ||||
|             } | ||||
|  | ||||
|             outputCount = 0; | ||||
|             var rows = []; | ||||
|             keys.forEach(function(p) { | ||||
|                 var row = $("#red-ui-editor-node-label-form-output-"+p).parent(); | ||||
|                 if (row.length === 0 && outputMap[p] !== -1) { | ||||
|                     if (childCount === 0) { | ||||
|                         $(children[0]).remove(); | ||||
|                         childCount = -1; | ||||
|                     } | ||||
|                     row = buildLabelRow("output",p,"",outputPlaceholder); | ||||
|                 } else { | ||||
|                     row.detach(); | ||||
|                 } | ||||
|                 if (outputMap[p] !== -1) { | ||||
|                     outputCount++; | ||||
|                     rows.push({i:parseInt(outputMap[p]),r:row}); | ||||
|                 } | ||||
|             }); | ||||
|             rows.sort(function(A,B) { | ||||
|                 return A.i-B.i; | ||||
|             }); | ||||
|             rows.forEach(function(r,i) { | ||||
|                 r.r.find("label").text((i+1)+"."); | ||||
|                 r.r.appendTo(outputsDiv); | ||||
|             }); | ||||
|             if (rows.length === 0) { | ||||
|                 buildLabelRow("output",i,"").appendTo(outputsDiv); | ||||
|             } else { | ||||
|  | ||||
|             } | ||||
|         } else { | ||||
|             outputCount = Math.max(0,parseInt(formOutputs)); | ||||
|         } | ||||
|         children = outputsDiv.children(); | ||||
|         childCount = children.length; | ||||
|         if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) { | ||||
|             childCount--; | ||||
|         } | ||||
|         if (childCount < outputCount) { | ||||
|             if (childCount === 0) { | ||||
|                 // remove the 'none' placeholder | ||||
|                 $(children[0]).remove(); | ||||
|             } | ||||
|             for (i = childCount;i<outputCount;i++) { | ||||
|                 buildLabelRow("output",i,"").appendTo(outputsDiv); | ||||
|             } | ||||
|         } else if (childCount > outputCount) { | ||||
|             for (i=outputCount;i<childCount;i++) { | ||||
|                 $(children[i]).remove(); | ||||
|             } | ||||
|             if (outputCount === 0) { | ||||
|                 buildLabelRow().appendTo(outputsDiv); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function buildLabelRow(type, index, value, placeHolder) { | ||||
|         var result = $('<div>',{class:"red-ui-editor-node-label-form-row"}); | ||||
|         if (type === undefined) { | ||||
|             $('<span>').text(RED._("editor.noDefaultLabel")).appendTo(result); | ||||
|             result.addClass("red-ui-editor-node-label-form-none"); | ||||
|         } else { | ||||
|             result.addClass(""); | ||||
|             var id = "red-ui-editor-node-label-form-"+type+"-"+index; | ||||
|             $('<label>',{for:id}).text((index+1)+".").appendTo(result); | ||||
|             var input = $('<input>',{type:"text",id:id, placeholder: placeHolder}).val(value).appendTo(result); | ||||
|             var clear = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').appendTo(result); | ||||
|             clear.on("click", function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 input.val(""); | ||||
|             }); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     function updateLabels(node, changes, outputMap) { | ||||
|         var inputLabels = $("#red-ui-editor-node-label-form-inputs").children().find("input"); | ||||
|         var outputLabels = $("#red-ui-editor-node-label-form-outputs").children().find("input"); | ||||
|  | ||||
|         var hasNonBlankLabel = false; | ||||
|         var changed = false; | ||||
|         var newValue = inputLabels.map(function() { | ||||
|             var v = $(this).val(); | ||||
|             hasNonBlankLabel = hasNonBlankLabel || v!== ""; | ||||
|             return v; | ||||
|         }).toArray().slice(0,node.inputs); | ||||
|         if ((node.inputLabels === undefined && hasNonBlankLabel) || | ||||
|             (node.inputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.inputLabels))) { | ||||
|             changes.inputLabels = node.inputLabels; | ||||
|             node.inputLabels = newValue; | ||||
|             changed = true; | ||||
|         } | ||||
|         hasNonBlankLabel = false; | ||||
|         newValue = new Array(node.outputs); | ||||
|         outputLabels.each(function() { | ||||
|             var index = $(this).attr('id').substring("red-ui-editor-node-label-form-output-".length); | ||||
|             if (outputMap && outputMap.hasOwnProperty(index)) { | ||||
|                 index = parseInt(outputMap[index]); | ||||
|                 if (index === -1) { | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             var v = $(this).val(); | ||||
|             hasNonBlankLabel = hasNonBlankLabel || v!== ""; | ||||
|  | ||||
|             // mark changed output port labels as dirty | ||||
|             if (node.type === "subflow" && (!node.outputLabels || node.outputLabels[index] !== v)) { | ||||
|                 node.out[index].dirty = true; | ||||
|             } | ||||
|  | ||||
|             newValue[index] = v; | ||||
|         }); | ||||
|  | ||||
|         if ((node.outputLabels === undefined && hasNonBlankLabel) || | ||||
|             (node.outputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.outputLabels))) { | ||||
|             changes.outputLabels = node.outputLabels; | ||||
|             node.outputLabels = newValue; | ||||
|             changed = true; | ||||
|  | ||||
|             // trigger redraw of dirty port labels | ||||
|             if (node.type === "subflow") { | ||||
|                 RED.view.redraw(); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
|  | ||||
|  | ||||
| })(); | ||||
							
								
								
									
										67
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/description.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/description.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| ;(function() { | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-description", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.description"), | ||||
|             name: RED._("editor-tab.description"), | ||||
|             iconClass: "fa fa-file-text-o", | ||||
|  | ||||
|             create: function(container) { | ||||
|                 this.editor = buildDescriptionForm(container,node); | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|                 this.editor.resize(); | ||||
|             }, | ||||
|             close: function() { | ||||
|                 this.editor.destroy(); | ||||
|                 this.editor = null; | ||||
|             }, | ||||
|             show: function() { | ||||
|                 this.editor.focus(); | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 var oldInfo = node.info; | ||||
|                 var newInfo = this.editor.getValue(); | ||||
|                 if (!!oldInfo) { | ||||
|                     // Has existing info property | ||||
|                     if (newInfo.trim() === "") { | ||||
|                         // New value is blank - remove the property | ||||
|                         editState.changed = true; | ||||
|                         editState.changes.info = oldInfo; | ||||
|                         delete node.info; | ||||
|                     } else if (newInfo !== oldInfo) { | ||||
|                         // New value is different | ||||
|                         editState.changed = true; | ||||
|                         editState.changes.info = oldInfo; | ||||
|                         node.info = newInfo; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // No existing info | ||||
|                     if (newInfo.trim() !== "") { | ||||
|                         // New value is not blank | ||||
|                         editState.changed = true; | ||||
|                         editState.changes.info = undefined; | ||||
|                         node.info = newInfo; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     function buildDescriptionForm(container,node) { | ||||
|         var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container); | ||||
|         var toolbarRow = $('<div></div>').appendTo(dialogForm); | ||||
|         var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: 100%"></div>').appendTo(dialogForm); | ||||
|         var editorId = "node-info-input-info-editor-"+Math.floor(1000*Math.random()); | ||||
|         $('<div style="height: 100%" class="node-text-editor" id="'+editorId+'" ></div>').appendTo(row); | ||||
|         var nodeInfoEditor = RED.editor.createEditor({ | ||||
|             id: editorId, | ||||
|             mode: 'ace/mode/markdown', | ||||
|             stateId: RED.editor.generateViewStateId("node", node, "nodeinfo"), | ||||
|             value: node.info || "" | ||||
|         }); | ||||
|         node.infoEditor = nodeInfoEditor; | ||||
|         return nodeInfoEditor; | ||||
|     } | ||||
|  | ||||
| })(); | ||||
							
								
								
									
										75
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| ;(function() { | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-envProperties", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.envProperties"), | ||||
|             name: RED._("editor-tab.envProperties"), | ||||
|             iconClass: "fa fa-list", | ||||
|             create: function(container) { | ||||
|                 var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container); | ||||
|                 var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form); | ||||
|                 this.list = $('<ol></ol>').appendTo(listContainer); | ||||
|                 RED.editor.envVarList.create(this.list, node); | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|                 this.list.editableList('height',size.height); | ||||
|             }, | ||||
|             close: function() { | ||||
|  | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 var old_env = node.env; | ||||
|                 var new_env = []; | ||||
|                 if (/^subflow:/.test(node.type)) { | ||||
|                     new_env = RED.subflow.exportSubflowInstanceEnv(node); | ||||
|                 } | ||||
|  | ||||
|                 // Get the values from the Properties table tab | ||||
|                 var items = this.list.editableList('items'); | ||||
|                 items.each(function (i,el) { | ||||
|                     var data = el.data('data'); | ||||
|                     var item; | ||||
|                     if (data.nameField && data.valueField) { | ||||
|                         item = { | ||||
|                             name: data.nameField.val(), | ||||
|                             value: data.valueField.typedInput("value"), | ||||
|                             type: data.valueField.typedInput("type") | ||||
|                         } | ||||
|                         if (item.name.trim() !== "") { | ||||
|                             new_env.push(item); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|  | ||||
|                 if (new_env && new_env.length > 0) { | ||||
|                     new_env.forEach(function(prop) { | ||||
|                         if (prop.type === "cred") { | ||||
|                             node.credentials = node.credentials || {_:{}}; | ||||
|                             node.credentials[prop.name] = prop.value; | ||||
|                             node.credentials['has_'+prop.name] = (prop.value !== ""); | ||||
|                             if (prop.value !== '__PWRD__') { | ||||
|                                 editState.changed = true; | ||||
|                             } | ||||
|                             delete prop.value; | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|                 if (!old_env && new_env.length === 0) { | ||||
|                     delete node.env; | ||||
|                 } else if (!isSameObj(old_env, new_env)) { | ||||
|                     editState.changes.env = node.env; | ||||
|                     if (new_env.length === 0) { | ||||
|                         delete node.env; | ||||
|                     } else { | ||||
|                         node.env = new_env; | ||||
|                     } | ||||
|                     editState.changed = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|     function isSameObj(env0, env1) { | ||||
|         return (JSON.stringify(env0) === JSON.stringify(env1)); | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										60
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| ;(function() { | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-flow-properties", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.properties"), | ||||
|             name: RED._("editor-tab.properties"), | ||||
|             iconClass: "fa fa-cog", | ||||
|             create: function(container) { | ||||
|                 var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(container); | ||||
|                 $('<div class="form-row">'+ | ||||
|                   '<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+ | ||||
|                   '<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+ | ||||
|                   '</div>').appendTo(dialogForm); | ||||
|  | ||||
|                 var row = $('<div class="form-row node-text-editor-row">'+ | ||||
|                             '<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+ | ||||
|                             '<div style="min-height:150px;" class="node-text-editor" id="node-input-info"></div>'+ | ||||
|                             '</div>').appendTo(dialogForm); | ||||
|                 this.tabflowEditor = RED.editor.createEditor({ | ||||
|                     id: 'node-input-info', | ||||
|                     mode: 'ace/mode/markdown', | ||||
|                     value: "" | ||||
|                 }); | ||||
|  | ||||
|                 $('<input type="text" style="display: none;" />').prependTo(dialogForm); | ||||
|                 dialogForm.on("submit", function(e) { e.preventDefault();}); | ||||
|  | ||||
|                 $("#node-input-name").val(node.label); | ||||
|                 RED.text.bidi.prepareInput($("#node-input-name")); | ||||
|                 this.tabflowEditor.getSession().setValue(node.info || "", -1); | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|                 $("#node-input-info").css("height", (size.height-70)+"px"); | ||||
|                 this.tabflowEditor.resize(); | ||||
|             }, | ||||
|             close: function() { | ||||
|                 this.tabflowEditor.destroy(); | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 var label = $( "#node-input-name" ).val(); | ||||
|  | ||||
|                 if (node.label != label) { | ||||
|                     editState.changes.label = node.label; | ||||
|                     editState.changed = true; | ||||
|                     node.label = label; | ||||
|                 } | ||||
|  | ||||
|                 var info = this.tabflowEditor.getValue(); | ||||
|                 if (node.info !== info) { | ||||
|                     editState.changes.info = node.info; | ||||
|                     editState.changed = true; | ||||
|                     node.info = info; | ||||
|                 } | ||||
|                 $("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled); | ||||
|                 $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.disabled); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| })(); | ||||
							
								
								
									
										198
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| ;(function() { | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-properties", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.properties"), | ||||
|             name: RED._("editor-tab.properties"), | ||||
|             iconClass: "fa fa-cog", | ||||
|             create: function(container) { | ||||
|  | ||||
|                 var nodeType = node.type; | ||||
|                 if (node.type === "subflow") { | ||||
|                     nodeType = "subflow-template"; | ||||
|                 } else if (node.type.substring(0,8) == "subflow:") { | ||||
|                     nodeType = "subflow"; | ||||
|                 } | ||||
|  | ||||
|                 var i18nNamespace; | ||||
|                 if (node._def.set.module === "node-red") { | ||||
|                     i18nNamespace = "node-red"; | ||||
|                 } else { | ||||
|                     i18nNamespace = node._def.set.id; | ||||
|                 } | ||||
|  | ||||
|                 var formStyle = "dialog-form"; | ||||
|                 this.inputClass = "node-input"; | ||||
|                 if (node._def.category === "config" && nodeType !== "group") { | ||||
|                     this.inputClass = "node-config-input"; | ||||
|                     formStyle = "node-config-dialog-edit-form"; | ||||
|                 } | ||||
|                 RED.editor.buildEditForm(container,formStyle,nodeType,i18nNamespace,node); | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|                 if (node && node._def.oneditresize) { | ||||
|                     try { | ||||
|                         node._def.oneditresize.call(node,size); | ||||
|                     } catch(err) { | ||||
|                         console.log("oneditresize",node.id,node.type,err.toString()); | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             close: function() { | ||||
|  | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 var newValue; | ||||
|                 var d; | ||||
|                 if (node._def.defaults) { | ||||
|                     for (d in node._def.defaults) { | ||||
|                         if (node._def.defaults.hasOwnProperty(d)) { | ||||
|                             var input = $("#"+this.inputClass+"-"+d); | ||||
|                             if (input.attr('type') === "checkbox") { | ||||
|                                 newValue = input.prop('checked'); | ||||
|                             } else if (input.prop("nodeName") === "select" && input.attr("multiple") === "multiple") { | ||||
|                                 // An empty select-multiple box returns null. | ||||
|                                 // Need to treat that as an empty array. | ||||
|                                 newValue = input.val(); | ||||
|                                 if (newValue == null) { | ||||
|                                     newValue = []; | ||||
|                                 } | ||||
|                             } else if ("format" in node._def.defaults[d] && node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") { | ||||
|                                 newValue = input.text(); | ||||
|                             } else { | ||||
|                                 newValue = input.val(); | ||||
|                             } | ||||
|                             if (newValue != null) { | ||||
|                                 if (d === "outputs") { | ||||
|                                     if  (newValue.trim() === "") { | ||||
|                                         continue; | ||||
|                                     } | ||||
|                                     if (isNaN(newValue)) { | ||||
|                                         editState.outputMap = JSON.parse(newValue); | ||||
|                                         var outputCount = 0; | ||||
|                                         var outputsChanged = false; | ||||
|                                         var keys = Object.keys(editState.outputMap); | ||||
|                                         keys.forEach(function(p) { | ||||
|                                             if (isNaN(p)) { | ||||
|                                                 // New output; | ||||
|                                                 outputCount ++; | ||||
|                                                 delete editState.outputMap[p]; | ||||
|                                             } else { | ||||
|                                                 editState.outputMap[p] = editState.outputMap[p]+""; | ||||
|                                                 if (editState.outputMap[p] !== "-1") { | ||||
|                                                     outputCount++; | ||||
|                                                     if (editState.outputMap[p] !== p) { | ||||
|                                                         // Output moved | ||||
|                                                         outputsChanged = true; | ||||
|                                                     } else { | ||||
|                                                         delete editState.outputMap[p]; | ||||
|                                                     } | ||||
|                                                 } else { | ||||
|                                                     // Output removed | ||||
|                                                     outputsChanged = true; | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         }); | ||||
|  | ||||
|                                         newValue = outputCount; | ||||
|                                         if (outputsChanged) { | ||||
|                                             editState.changed = true; | ||||
|                                         } | ||||
|                                     } else { | ||||
|                                         newValue = parseInt(newValue); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 if (node._def.defaults[d].type) { | ||||
|                                     if (newValue == "_ADD_") { | ||||
|                                         newValue = ""; | ||||
|                                     } | ||||
|                                 } | ||||
|                                 if (!isEqual(node[d], newValue)) { | ||||
|                                     if (node._def.defaults[d].type) { | ||||
|                                         // Change to a related config node | ||||
|                                         var configNode = RED.nodes.node(node[d]); | ||||
|                                         if (configNode) { | ||||
|                                             var users = configNode.users; | ||||
|                                             users.splice(users.indexOf(node),1); | ||||
|                                             RED.events.emit("nodes:change",configNode); | ||||
|                                         } | ||||
|                                         configNode = RED.nodes.node(newValue); | ||||
|                                         if (configNode) { | ||||
|                                             configNode.users.push(node); | ||||
|                                             RED.events.emit("nodes:change",configNode); | ||||
|                                         } | ||||
|                                     } | ||||
|                                     editState.changes[d] = node[d]; | ||||
|                                     node[d] = newValue; | ||||
|                                     editState.changed = true; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (node._def.credentials) { | ||||
|                     var credDefinition = node._def.credentials; | ||||
|                     var credsChanged = updateNodeCredentials(node,credDefinition,this.inputClass); | ||||
|                     editState.changed = editState.changed || credsChanged; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     /** | ||||
|      * Compares `newValue` with `originalValue` for equality.   | ||||
|      * @param {*} originalValue Original value | ||||
|      * @param {*} newValue New value | ||||
|      * @returns {boolean} true if originalValue equals newValue, otherwise false | ||||
|      */ | ||||
|      function isEqual(originalValue, newValue) { | ||||
|         try { | ||||
|             if(originalValue == newValue) { | ||||
|                 return true;  | ||||
|             } | ||||
|             return JSON.stringify(originalValue) === JSON.stringify(newValue); | ||||
|         } catch (err) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the node credentials from the edit form | ||||
|      * @param node - the node containing the credentials | ||||
|      * @param credDefinition - definition of the credentials | ||||
|      * @param prefix - prefix of the input fields | ||||
|      * @return {boolean} whether anything has changed | ||||
|      */ | ||||
|     function updateNodeCredentials(node, credDefinition, prefix) { | ||||
|         var changed = false; | ||||
|         if (!node.credentials) { | ||||
|             node.credentials = {_:{}}; | ||||
|         } else if (!node.credentials._) { | ||||
|             node.credentials._ = {}; | ||||
|         } | ||||
|  | ||||
|         for (var cred in credDefinition) { | ||||
|             if (credDefinition.hasOwnProperty(cred)) { | ||||
|                 var input = $("#" + prefix + '-' + cred); | ||||
|                 if (input.length > 0) { | ||||
|                     var value = input.val(); | ||||
|                     if (credDefinition[cred].type == 'password') { | ||||
|                         node.credentials['has_' + cred] = (value !== ""); | ||||
|                         if (value == '__PWRD__') { | ||||
|                             continue; | ||||
|                         } | ||||
|                         changed = true; | ||||
|  | ||||
|                     } | ||||
|                     node.credentials[cred] = value; | ||||
|                     if (value != node.credentials._[cred]) { | ||||
|                         changed = true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
|  | ||||
|  | ||||
| })(); | ||||
							
								
								
									
										179
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowModule.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowModule.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| (function() { | ||||
|     var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|     '</form>'; | ||||
|  | ||||
|     RED.editor.registerEditPane("editor-tab-subflow-module", function(node) { | ||||
|         return { | ||||
|             label: RED._("editor-tab.module"), | ||||
|             name: RED._("editor-tab.module"), | ||||
|             iconClass: "fa fa-cube", | ||||
|             create: function(container) { | ||||
|                 buildModuleForm(container, node); | ||||
|             }, | ||||
|             resize: function(size) { | ||||
|             }, | ||||
|             close: function() { | ||||
|  | ||||
|             }, | ||||
|             apply: function(editState) { | ||||
|                 var newMeta = exportSubflowModuleProperties(node); | ||||
|                 if (!isSameObj(node.meta,newMeta)) { | ||||
|                     editState.changes.meta = node.meta; | ||||
|                     node.meta = newMeta; | ||||
|                     editState.changed = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     function isSameObj(env0, env1) { | ||||
|         return (JSON.stringify(env0) === JSON.stringify(env1)); | ||||
|     } | ||||
|  | ||||
|     function setupInputValidation(input,validator) { | ||||
|         var errorTip; | ||||
|         var validateTimeout; | ||||
|  | ||||
|         var validateFunction = function() { | ||||
|             if (validateTimeout) { | ||||
|                 return; | ||||
|             } | ||||
|             validateTimeout = setTimeout(function() { | ||||
|                 var error = validator(input.val()); | ||||
|                 // if (!error && errorTip) { | ||||
|                 //     errorTip.close(); | ||||
|                 //     errorTip = null; | ||||
|                 // } else if (error && !errorTip) { | ||||
|                 //     errorTip = RED.popover.create({ | ||||
|                 //         tooltip: true, | ||||
|                 //         target:input, | ||||
|                 //         size: "small", | ||||
|                 //         direction: "bottom", | ||||
|                 //         content: error, | ||||
|                 //     }).open(); | ||||
|                 // } | ||||
|                 input.toggleClass("input-error",!!error); | ||||
|                 validateTimeout = null; | ||||
|             }) | ||||
|         } | ||||
|         input.on("change keyup paste", validateFunction); | ||||
|     } | ||||
|  | ||||
|     function buildModuleForm(container, node) { | ||||
|         $(_subflowModulePaneTemplate).appendTo(container); | ||||
|         var moduleProps = node.meta || {}; | ||||
|         [ | ||||
|             'module', | ||||
|             'type', | ||||
|             'version', | ||||
|             'author', | ||||
|             'desc', | ||||
|             'keywords', | ||||
|             'license' | ||||
|         ].forEach(function(property) { | ||||
|             $("#subflow-input-module-"+property).val(moduleProps[property]||"") | ||||
|         }) | ||||
|         $("#subflow-input-module-type").attr("placeholder",node.id); | ||||
|  | ||||
|         setupInputValidation($("#subflow-input-module-module"), function(newValue) { | ||||
|             newValue = newValue.trim(); | ||||
|             var isValid = newValue.length < 215; | ||||
|             isValid = isValid && !/^[._]/.test(newValue); | ||||
|             isValid = isValid && !/[A-Z]/.test(newValue); | ||||
|             if (newValue !== encodeURIComponent(newValue)) { | ||||
|                 var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue); | ||||
|                 if (m) { | ||||
|                     isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2])) | ||||
|                 } else { | ||||
|                     isValid = false; | ||||
|                 } | ||||
|             } | ||||
|             return isValid?"":"Invalid module name" | ||||
|         }) | ||||
|         setupInputValidation($("#subflow-input-module-version"), function(newValue) { | ||||
|             newValue = newValue.trim(); | ||||
|             var isValid = newValue === "" || | ||||
|                           /^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue); | ||||
|             return isValid?"":"Invalid version number" | ||||
|         }) | ||||
|  | ||||
|         var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"]; | ||||
|         var typedLicenses = { | ||||
|             types: licenses.map(function(l) { | ||||
|                 return { | ||||
|                     value: l, | ||||
|                     label: l === "none" ? RED._("editor:subflow.licenseNone") : l, | ||||
|                     hasValue: false | ||||
|                 }; | ||||
|             }) | ||||
|         } | ||||
|         typedLicenses.types.push({ | ||||
|             value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg" | ||||
|         }) | ||||
|         if (!moduleProps.license) { | ||||
|             typedLicenses.default = "none"; | ||||
|         } else if (licenses.indexOf(moduleProps.license) > -1) { | ||||
|             typedLicenses.default = moduleProps.license; | ||||
|         } else { | ||||
|             typedLicenses.default = "_custom_"; | ||||
|         } | ||||
|         $("#subflow-input-module-license").typedInput(typedLicenses) | ||||
|     } | ||||
|     function exportSubflowModuleProperties(node) { | ||||
|         var value; | ||||
|         var moduleProps = {}; | ||||
|         [ | ||||
|             'module', | ||||
|             'type', | ||||
|             'version', | ||||
|             'author', | ||||
|             'desc', | ||||
|             'keywords' | ||||
|         ].forEach(function(property) { | ||||
|             value = $("#subflow-input-module-"+property).val().trim(); | ||||
|             if (value) { | ||||
|                 moduleProps[property] = value; | ||||
|             } | ||||
|         }) | ||||
|         var selectedLicenseType = $("#subflow-input-module-license").typedInput("type"); | ||||
|  | ||||
|         if (selectedLicenseType === '_custom_') { | ||||
|             value = $("#subflow-input-module-license").val(); | ||||
|             if (value) { | ||||
|                 moduleProps.license = value; | ||||
|             } | ||||
|         } else if (selectedLicenseType !== "none") { | ||||
|             moduleProps.license = selectedLicenseType; | ||||
|         } | ||||
|         return moduleProps; | ||||
|     } | ||||
|  | ||||
| })(); | ||||
| @@ -21,6 +21,7 @@ | ||||
|     var definition = { | ||||
|         show: function(options) { | ||||
|             var value = options.value; | ||||
|             var onCancel = options.cancel; | ||||
|             var onComplete = options.complete; | ||||
|             var type = "_text" | ||||
|             if ($("script[data-template-name='"+type+"']").length === 0) { | ||||
| @@ -28,16 +29,16 @@ | ||||
|             } | ||||
|             RED.view.state(RED.state.EDITING); | ||||
|             var expressionEditor; | ||||
|             var changeTimer; | ||||
|  | ||||
|             var trayOptions = { | ||||
|                 title: options.title, | ||||
|                 focusElement: options.focusElement, | ||||
|                 width: options.width||"inherit", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "node-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             if(onCancel) { onCancel(); } | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -46,7 +47,8 @@ | ||||
|                         text: RED._("common.label.done"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                             onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition()); | ||||
|                             expressionEditor.saveView(); | ||||
|                             if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor);} | ||||
|                             RED.tray.close(); | ||||
|                         } | ||||
|                     } | ||||
| @@ -55,31 +57,27 @@ | ||||
|                     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('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|                     expressionEditor = RED.editor.createEditor({ | ||||
|                         id: 'node-input-text', | ||||
|                         value: "", | ||||
|                         mode:"ace/mode/"+(options.mode||"text") | ||||
|                         value: value||"", | ||||
|                         stateId: options.stateId, | ||||
|                         mode:"ace/mode/"+(options.mode||"text"), | ||||
|                         focus: true, | ||||
|                     }); | ||||
|                     expressionEditor.getSession().setValue(value||"",-1); | ||||
|                     if (options.cursor) { | ||||
|                     if (options.cursor && !expressionEditor._initState) { | ||||
|                         expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); | ||||
|                     } | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     expressionEditor.destroy(); | ||||
|                     if (options.onclose) { | ||||
|                         options.onclose(); | ||||
|                     } | ||||
|                     expressionEditor.destroy(); | ||||
|                 }, | ||||
|                 show: function() {} | ||||
|             } | ||||
|   | ||||
| @@ -73,6 +73,7 @@ RED.eventLog = (function() { | ||||
|                     var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|                     eventLogEditor = RED.editor.createEditor({ | ||||
|                         mode:"ace/mode/shell", | ||||
|                         id: 'red-ui-event-log-editor', | ||||
|                         value: backlog.join("\n"), | ||||
|                         lineNumbers: false, | ||||
|   | ||||
| @@ -87,16 +87,18 @@ RED.group = (function() { | ||||
|         "label-position": "nw" | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     var groupDef = { | ||||
|         defaults:{ | ||||
|             name:{value:""}, | ||||
|             style:{value:{label:true}}, | ||||
|             nodes:{value:[]} | ||||
|             nodes:{value:[]}, | ||||
|             env: {value:[]}, | ||||
|         }, | ||||
|         category: "config", | ||||
|         oneditprepare: function() { | ||||
|             var style = this.style || {}; | ||||
|             RED.colorPicker.create({ | ||||
|             RED.editor.colorPicker.create({ | ||||
|                 id:"node-input-style-stroke", | ||||
|                 value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4", | ||||
|                 palette: colorPalette, | ||||
| @@ -107,7 +109,7 @@ RED.group = (function() { | ||||
|                 none: true, | ||||
|                 opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0) | ||||
|             }).appendTo("#node-input-row-style-stroke"); | ||||
|             RED.colorPicker.create({ | ||||
|             RED.editor.colorPicker.create({ | ||||
|                 id:"node-input-style-fill", | ||||
|                 value: style.fill || defaultGroupStyle.fill ||"none", | ||||
|                 palette: colorPalette, | ||||
| @@ -124,7 +126,7 @@ RED.group = (function() { | ||||
|                 value:style["label-position"] || "nw" | ||||
|             }).appendTo("#node-input-row-style-label-position"); | ||||
|  | ||||
|             RED.colorPicker.create({ | ||||
|             RED.editor.colorPicker.create({ | ||||
|                 id:"node-input-style-color", | ||||
|                 value: style.color || defaultGroupStyle.color ||"#a4a4a4", | ||||
|                 palette: colorPalette, | ||||
| @@ -144,7 +146,6 @@ RED.group = (function() { | ||||
|             }) | ||||
|             $("#node-input-style-label").prop("checked", this.style.label) | ||||
|             $("#node-input-style-label").trigger("change"); | ||||
|  | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|         }, | ||||
| @@ -183,7 +184,9 @@ RED.group = (function() { | ||||
|             var activateUngroup = false; | ||||
|             var activateMerge = false; | ||||
|             var activateRemove = false; | ||||
|             var singleGroupSelected = false; | ||||
|             if (activateGroup) { | ||||
|                 singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group'; | ||||
|                 selection.nodes.forEach(function (n) { | ||||
|                     if (n.type === "group") { | ||||
|                         activateUngroup = true; | ||||
| @@ -200,6 +203,8 @@ RED.group = (function() { | ||||
|             RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup); | ||||
|             RED.menu.setDisabled("menu-item-group-merge", !activateMerge); | ||||
|             RED.menu.setDisabled("menu-item-group-remove", !activateRemove); | ||||
|             RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected); | ||||
|             RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup); | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:group-selection", function() { groupSelection() }) | ||||
| @@ -252,6 +257,7 @@ RED.group = (function() { | ||||
|         if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') { | ||||
|             groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style)); | ||||
|             RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"}) | ||||
|             RED.menu.setDisabled("menu-item-edit-paste-group-style", false) | ||||
|         } | ||||
|     } | ||||
|     function pasteGroupStyle() { | ||||
| @@ -317,9 +323,6 @@ RED.group = (function() { | ||||
|                 groups: [ ], | ||||
|                 dirty: RED.nodes.dirty() | ||||
|             } | ||||
|             RED.history.push(historyEvent); | ||||
|  | ||||
|  | ||||
|             groups.forEach(function(g) { | ||||
|                 newSelection = newSelection.concat(ungroup(g)) | ||||
|                 historyEvent.groups.push(g); | ||||
| @@ -346,8 +349,10 @@ RED.group = (function() { | ||||
|             } | ||||
|             if (n.type === 'group') { | ||||
|                 RED.events.emit("groups:change",n) | ||||
|             } else { | ||||
|             } else if (n.type !== 'junction') { | ||||
|                 RED.events.emit("nodes:change",n) | ||||
|             } else { | ||||
|                 RED.events.emit("junctions:change",n) | ||||
|             } | ||||
|         }) | ||||
|         RED.nodes.removeGroup(g); | ||||
| @@ -541,8 +546,10 @@ RED.group = (function() { | ||||
|                 group.h = Math.max(group.h,n.y+n.h/2+25-group.y); | ||||
|                 if (n.type === 'group') { | ||||
|                     RED.events.emit("groups:change",n) | ||||
|                 } else { | ||||
|                 } else if (n.type !== 'junction') { | ||||
|                     RED.events.emit("nodes:change",n) | ||||
|                 } else { | ||||
|                     RED.events.emit("junctions:change",n) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -577,19 +584,23 @@ RED.group = (function() { | ||||
|             } | ||||
|             if (n.type === 'group') { | ||||
|                 RED.events.emit("groups:change",n) | ||||
|             } else { | ||||
|             } else if (n.type !== 'junction') { | ||||
|                 RED.events.emit("nodes:change",n) | ||||
|             } else { | ||||
|                 RED.events.emit("junctions:change",n) | ||||
|             } | ||||
|         } | ||||
|         markDirty(group); | ||||
|     } | ||||
|  | ||||
|     function getNodes(group,recursive) { | ||||
|     function getNodes(group,recursive,excludeGroup) { | ||||
|         var nodes = []; | ||||
|         group.nodes.forEach(function(n) { | ||||
|             nodes.push(n); | ||||
|             if (n.type !== 'group' || !excludeGroup) { | ||||
|                 nodes.push(n); | ||||
|             } | ||||
|             if (recursive && n.type === 'group') { | ||||
|                 nodes = nodes.concat(getNodes(n,recursive)) | ||||
|                 nodes = nodes.concat(getNodes(n,recursive,excludeGroup)) | ||||
|             } | ||||
|         }) | ||||
|         return nodes; | ||||
|   | ||||
| @@ -49,15 +49,15 @@ RED.keyboard = (function() { | ||||
|         "]": 221, | ||||
|         "{": 219,// <- QWERTY specific | ||||
|         "}": 221 // <- QWERTY specific | ||||
|     } | ||||
|     }; | ||||
|     var metaKeyCodes = { | ||||
|         16: true, | ||||
|         17: true, | ||||
|         18: true, | ||||
|         91: true, | ||||
|         93: true | ||||
|     } | ||||
|     var actionToKeyMap = {} | ||||
|     }; | ||||
|     var actionToKeyMap = {}; | ||||
|     var defaultKeyMap = {}; | ||||
|  | ||||
|     // FF generates some different keycodes because reasons. | ||||
| @@ -65,7 +65,7 @@ RED.keyboard = (function() { | ||||
|         59:186, | ||||
|         61:187, | ||||
|         173:189 | ||||
|     } | ||||
|     }; | ||||
|  | ||||
|     function migrateOldKeymap() { | ||||
|         // pre-0.18 | ||||
| @@ -80,7 +80,7 @@ RED.keyboard = (function() { | ||||
|     } | ||||
|  | ||||
|     function getUserKey(action) { | ||||
|         return RED.settings.get('editor.keymap',{})[action] | ||||
|         return RED.settings.get('editor.keymap',{})[action]; | ||||
|     } | ||||
|  | ||||
|     function mergeKeymaps(defaultKeymap, themeKeymap) { | ||||
| @@ -105,7 +105,7 @@ RED.keyboard = (function() { | ||||
|                                 scope:scope, | ||||
|                                 key:key, | ||||
|                                 user:false | ||||
|                             }) | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -115,13 +115,13 @@ RED.keyboard = (function() { | ||||
|             if (themeKeymap.hasOwnProperty(action)) { | ||||
|                 if (!themeKeymap[action].key) { | ||||
|                     // No key for this action - default is no keybinding | ||||
|                     delete mergedKeymap[action] | ||||
|                     delete mergedKeymap[action]; | ||||
|                 } else { | ||||
|                     mergedKeymap[action] = [{ | ||||
|                         scope: themeKeymap[action].scope || "*", | ||||
|                         key: themeKeymap[action].key, | ||||
|                         user: false | ||||
|                     }] | ||||
|                     }]; | ||||
|                     if (mergedKeymap[action][0].scope === "workspace") { | ||||
|                         mergedKeymap[action][0].scope = "red-ui-workspace"; | ||||
|                     } | ||||
| @@ -131,7 +131,7 @@ RED.keyboard = (function() { | ||||
|         return mergedKeymap; | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|     function init(done) { | ||||
|         // Migrate from pre-0.18 | ||||
|         migrateOldKeymap(); | ||||
|  | ||||
| @@ -164,6 +164,7 @@ RED.keyboard = (function() { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             done(); | ||||
|         }); | ||||
|  | ||||
|         RED.userSettings.add({ | ||||
| @@ -174,8 +175,11 @@ RED.keyboard = (function() { | ||||
|                 setTimeout(function() { | ||||
|                     $("#red-ui-settings-tab-keyboard-filter").trigger("focus"); | ||||
|                 },200); | ||||
|             }, | ||||
|             close: function() { | ||||
|                 RED.menu.refreshShortcuts(); | ||||
|             } | ||||
|         }) | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function revertToDefault(action) { | ||||
| @@ -239,7 +243,13 @@ RED.keyboard = (function() { | ||||
|  | ||||
|     function resolveKeyEvent(evt) { | ||||
|         var slot = partialState||handlers; | ||||
|         if (evt.ctrlKey || evt.metaKey) { | ||||
|         // We cheat with MacOS CMD key and consider it the same as Ctrl. | ||||
|         // That means we don't have to have separate keymaps for different OS. | ||||
|         // It mostly works. | ||||
|         // One exception is shortcuts that include both Cmd and Ctrl. We don't | ||||
|         // support them - but we need to make sure we don't block browser-specific | ||||
|         // shortcuts (such as Cmd-Ctrl-F for fullscreen). | ||||
|         if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) { | ||||
|             slot = slot.ctrl; | ||||
|         } | ||||
|         if (slot && evt.shiftKey) { | ||||
| @@ -317,7 +327,7 @@ RED.keyboard = (function() { | ||||
|                         scope:scope, | ||||
|                         key:key, | ||||
|                         user:false | ||||
|                     } | ||||
|                     }; | ||||
|                 } | ||||
|                 if (!ondown) { | ||||
|                     var userAction = getUserKey(cbdown); | ||||
| @@ -340,7 +350,7 @@ RED.keyboard = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             keys.push([key,mod]) | ||||
|             keys.push([key,mod]); | ||||
|         } | ||||
|         var slot = handlers; | ||||
|         for (i=0;i<keys.length;i++) { | ||||
| @@ -363,7 +373,7 @@ RED.keyboard = (function() { | ||||
|             //slot[key] = {scope: scope, ondown:cbdown}; | ||||
|         } | ||||
|         slot.handlers = slot.handlers || []; | ||||
|         slot.handlers.push({scope:scope,ondown:cbdown}) | ||||
|         slot.handlers.push({scope:scope,ondown:cbdown}); | ||||
|         slot.scope = scope; | ||||
|         slot.ondown = cbdown; | ||||
|     } | ||||
| @@ -380,12 +390,12 @@ RED.keyboard = (function() { | ||||
|                 if (parsedKey) { | ||||
|                     keys.push(parsedKey); | ||||
|                 } else { | ||||
|                     console.log("Unrecognised key specifier:",key) | ||||
|                     console.log("Unrecognised key specifier:",key); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             keys.push([key,mod]) | ||||
|             keys.push([key,mod]); | ||||
|         } | ||||
|         var slot = handlers; | ||||
|         for (i=0;i<keys.length;i++) { | ||||
| @@ -407,7 +417,7 @@ RED.keyboard = (function() { | ||||
|         } | ||||
|         if (typeof slot.ondown === "string") { | ||||
|             if (typeof modifiers === 'boolean' && modifiers) { | ||||
|                 actionToKeyMap[slot.ondown] = {user: modifiers} | ||||
|                 actionToKeyMap[slot.ondown] = {user: modifiers}; | ||||
|             } else { | ||||
|                 delete actionToKeyMap[slot.ondown]; | ||||
|             } | ||||
| @@ -423,11 +433,11 @@ RED.keyboard = (function() { | ||||
|     function formatKey(key,plain) { | ||||
|         var formattedKey = isMac?key.replace(/ctrl-?/,"⌘"):key; | ||||
|         formattedKey = isMac?formattedKey.replace(/alt-?/,"⌥"):key; | ||||
|         formattedKey = formattedKey.replace(/shift-?/,"⇧") | ||||
|         formattedKey = formattedKey.replace(/left/,"←") | ||||
|         formattedKey = formattedKey.replace(/up/,"↑") | ||||
|         formattedKey = formattedKey.replace(/right/,"→") | ||||
|         formattedKey = formattedKey.replace(/down/,"↓") | ||||
|         formattedKey = formattedKey.replace(/shift-?/,"⇧"); | ||||
|         formattedKey = formattedKey.replace(/left/,"←"); | ||||
|         formattedKey = formattedKey.replace(/up/,"↑"); | ||||
|         formattedKey = formattedKey.replace(/right/,"→"); | ||||
|         formattedKey = formattedKey.replace(/down/,"↓"); | ||||
|         if (plain) { | ||||
|             return formattedKey; | ||||
|         } | ||||
| @@ -451,7 +461,6 @@ RED.keyboard = (function() { | ||||
|         var container = $(this); | ||||
|         var object = container.data('data'); | ||||
|  | ||||
|  | ||||
|         if (!container.hasClass('keyboard-shortcut-entry-expanded')) { | ||||
|             endEditShortcut(); | ||||
|  | ||||
| @@ -475,7 +484,7 @@ RED.keyboard = (function() { | ||||
|                 } | ||||
|                 $(this).toggleClass("input-error",!valid); | ||||
|                 okButton.attr("disabled",!valid); | ||||
|             }) | ||||
|             }); | ||||
|  | ||||
|             var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="red-ui-workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope); | ||||
|             scopeSelect.i18n(); | ||||
| @@ -485,7 +494,7 @@ RED.keyboard = (function() { | ||||
|             scopeSelect.val(object.scope||'*'); | ||||
|             scopeSelect.on("change", function() { | ||||
|                 keyInput.trigger("change"); | ||||
|             }) | ||||
|             }); | ||||
|  | ||||
|             var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope); | ||||
|             var okButton = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-check"></i></button>').appendTo(div); | ||||
| @@ -511,10 +520,13 @@ RED.keyboard = (function() { | ||||
|                     id:object.id, | ||||
|                     scope:shortcut?shortcut.scope:undefined, | ||||
|                     key:shortcut?shortcut.key:undefined, | ||||
|                     user:shortcut?shortcut.user:undefined | ||||
|                 } | ||||
|                     user:shortcut?shortcut.user:undefined, | ||||
|  | ||||
|                     label: object.label, | ||||
|                     options: object.options, | ||||
|                 }; | ||||
|                 buildShortcutRow(container,obj); | ||||
|             }) | ||||
|             }); | ||||
|  | ||||
|             keyInput.trigger("focus"); | ||||
|         } | ||||
| @@ -549,7 +561,7 @@ RED.keyboard = (function() { | ||||
|                             delete object.scope; | ||||
|                         } else { | ||||
|                             keyDiv.parent().removeClass("keyboard-shortcut-entry-unassigned"); | ||||
|                             keyDiv.append(RED.keyboard.formatKey(key)) | ||||
|                             keyDiv.append(RED.keyboard.formatKey(key)); | ||||
|                             $("<span>").text(scope).appendTo(scopeDiv); | ||||
|                             object.key = key; | ||||
|                             object.scope = scope; | ||||
| @@ -562,7 +574,7 @@ RED.keyboard = (function() { | ||||
|                         userKeymap[object.id] = { | ||||
|                             scope:shortcut.scope, | ||||
|                             key:shortcut.key | ||||
|                         } | ||||
|                         }; | ||||
|                         RED.settings.set('editor.keymap',userKeymap); | ||||
|                     } | ||||
|                 } | ||||
| @@ -578,13 +590,7 @@ RED.keyboard = (function() { | ||||
|         var item = $('<div class="keyboard-shortcut-entry">').appendTo(container); | ||||
|         container.data('data',object); | ||||
|  | ||||
|         var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() { | ||||
|             if (arguments[5] === 0) { | ||||
|                 return arguments[2].toUpperCase(); | ||||
|             } else { | ||||
|                 return " "+arguments[4].toUpperCase(); | ||||
|             } | ||||
|         }); | ||||
|         var text = object.label; | ||||
|         var label = $('<div>').addClass("keyboard-shortcut-entry-text").text(text).appendTo(item); | ||||
|  | ||||
|         var user = $('<i class="fa fa-user"></i>').prependTo(label); | ||||
| @@ -619,14 +625,15 @@ RED.keyboard = (function() { | ||||
|         pane.find("#red-ui-settings-tab-keyboard-filter").searchBox({ | ||||
|             delay: 100, | ||||
|             change: function() { | ||||
|                 var filterValue = $(this).val().trim(); | ||||
|                 var filterValue = $(this).val().trim().toLowerCase(); | ||||
|                 if (filterValue === "") { | ||||
|                     shortcutList.editableList('filter', null); | ||||
|                 } else { | ||||
|                     filterValue = filterValue.replace(/\s/g,""); | ||||
|                     shortcutList.editableList('filter', function(data) { | ||||
|                         return data.id.toLowerCase().replace(/^.*:/,"").replace("-","").indexOf(filterValue) > -1; | ||||
|                     }) | ||||
|                         var label = data.label.toLowerCase(); | ||||
|                         return label.indexOf(filterValue) > -1; | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
| @@ -647,9 +654,9 @@ RED.keyboard = (function() { | ||||
|         }); | ||||
|         var shortcuts = RED.actions.list(); | ||||
|         shortcuts.sort(function(A,B) { | ||||
|             var Aid = A.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase(); | ||||
|             var Bid = B.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase(); | ||||
|             return Aid.localeCompare(Bid); | ||||
|             var Akey = A.label; | ||||
|             var Bkey = B.label; | ||||
|             return Akey.localeCompare(Bkey); | ||||
|         }); | ||||
|         knownShortcuts = new Set(); | ||||
|         shortcuts.forEach(function(s) { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ RED.library = (function() { | ||||
|                     '<div class="red-ui-panel" id="red-ui-library-dialog-load-browser"></div>'+ | ||||
|                     '<div class="red-ui-panel">'+ | ||||
|                         '<div id="red-ui-library-dialog-load-preview">'+ | ||||
|                             '<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-text"></div>'+ | ||||
|                             '<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-text" style="position:relative; height: 50%; overflow-y: hidden;"></div>'+ | ||||
|                             '<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-details">'+ | ||||
|                                 '<table id="red-ui-library-dialog-load-preview-details-table" class="red-ui-info-table"></table>'+ | ||||
|                             '</div>'+ | ||||
| @@ -216,21 +216,27 @@ RED.library = (function() { | ||||
|             { id:'node-input-'+options.type+'-menu-open-library', | ||||
|                 label: RED._("library.openLibrary"), | ||||
|                 onselect: function() { | ||||
|  | ||||
|                     libraryEditor = ace.edit('red-ui-library-dialog-load-preview-text',{ | ||||
|                         useWorker: false | ||||
|                     }); | ||||
|                     libraryEditor.setTheme("ace/theme/tomorrow"); | ||||
|                     if (options.mode) { | ||||
|                         libraryEditor.getSession().setMode(options.mode); | ||||
|                     } | ||||
|                     libraryEditor.setOptions({ | ||||
|                     var editorOpts = { | ||||
|                         id: 'red-ui-library-dialog-load-preview-text', | ||||
|                         mode: options.mode, | ||||
|                         readOnly: true, | ||||
|                         highlightActiveLine: false, | ||||
|                         highlightGutterLine: false | ||||
|                     }); | ||||
|                     libraryEditor.renderer.$cursorLayer.element.style.opacity=0; | ||||
|                     libraryEditor.$blockScrolling = Infinity; | ||||
|                         highlightGutterLine: false, | ||||
|                         contextmenu: false | ||||
|                     } | ||||
|                     libraryEditor = RED.editor.createEditor(editorOpts); //use red.editor | ||||
|                     if(libraryEditor.isACE) { | ||||
|                         if (options.mode) { | ||||
|                             libraryEditor.getSession().setMode(options.mode); | ||||
|                         } | ||||
|                         libraryEditor.setOptions({ | ||||
|                             readOnly: true, | ||||
|                             highlightActiveLine: false, | ||||
|                             highlightGutterLine: false | ||||
|                         }); | ||||
|                         libraryEditor.renderer.$cursorLayer.element.style.opacity=0; | ||||
|                         libraryEditor.$blockScrolling = Infinity; | ||||
|                     } | ||||
|  | ||||
|                     activeLibrary = options; | ||||
|                     var listing = []; | ||||
| @@ -530,7 +536,7 @@ RED.library = (function() { | ||||
|     //             evt.preventDefault(); | ||||
|     //             var icon = libraryFields['icon'].input.val() || ""; | ||||
|     //             var iconPath = (icon ? RED.utils.separateIconPath(icon) : {}); | ||||
|     //             RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) { | ||||
|     //             RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) { | ||||
|     //                 iconButton.empty(); | ||||
|     //                 var path = newIcon || ""; | ||||
|     //                 var newPath = RED.utils.separateIconPath(path); | ||||
| @@ -809,6 +815,7 @@ RED.library = (function() { | ||||
|                 open: function(e) { | ||||
|                     RED.keyboard.disable(); | ||||
|                     $(this).parent().find(".ui-dialog-titlebar-close").hide(); | ||||
|                     libraryEditor.resize(); | ||||
|                 }, | ||||
|                 close: function(e) { | ||||
|                     RED.keyboard.enable(); | ||||
|   | ||||
| @@ -45,6 +45,22 @@ RED.notifications = (function() { | ||||
|  | ||||
|     var persistentNotifications = {}; | ||||
|  | ||||
|     var shade = (function() { | ||||
|         var shadeUsers = 0; | ||||
|         return { | ||||
|             show: function() { | ||||
|                 shadeUsers++; | ||||
|                 $("#red-ui-full-shade").show(); | ||||
|             }, | ||||
|             hide: function() { | ||||
|                 shadeUsers--; | ||||
|                 if (shadeUsers === 0) { | ||||
|                     $("#red-ui-full-shade").hide(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     })(); | ||||
|  | ||||
|     var currentNotifications = []; | ||||
|     var c = 0; | ||||
|     function notify(msg,type,fixed,timeout) { | ||||
| @@ -54,6 +70,10 @@ RED.notifications = (function() { | ||||
|             fixed = options.fixed; | ||||
|             timeout = options.timeout; | ||||
|             type = options.type; | ||||
|         } else { | ||||
|             options.type = type; | ||||
|             options.fixed = fixed; | ||||
|             options.timeout = options.timeout; | ||||
|         } | ||||
|  | ||||
|         if (options.id && persistentNotifications.hasOwnProperty(options.id)) { | ||||
| @@ -62,7 +82,7 @@ RED.notifications = (function() { | ||||
|         } | ||||
|  | ||||
|         if (options.modal) { | ||||
|             $("#red-ui-full-shade").show(); | ||||
|             shade.show(); | ||||
|         } | ||||
|  | ||||
|         if (currentNotifications.length > 4) { | ||||
| @@ -79,6 +99,8 @@ RED.notifications = (function() { | ||||
|         var n = document.createElement("div"); | ||||
|         n.id="red-ui-notification-"+c; | ||||
|         n.className = "red-ui-notification"; | ||||
|         n.options = options; | ||||
|  | ||||
|         n.fixed = fixed; | ||||
|         if (type) { | ||||
|             n.className = "red-ui-notification red-ui-notification-"+type; | ||||
| @@ -115,7 +137,6 @@ RED.notifications = (function() { | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $("#red-ui-notifications").append(n); | ||||
|         if (!RED.notifications.hide) { | ||||
|             $(n).slideDown(300); | ||||
| @@ -141,8 +162,8 @@ RED.notifications = (function() { | ||||
|                 } else { | ||||
|                     nn.parentNode.removeChild(nn); | ||||
|                 } | ||||
|                 if (options.modal) { | ||||
|                     $("#red-ui-full-shade").hide(); | ||||
|                 if (nn.options.modal) { | ||||
|                     shade.hide(); | ||||
|                 } | ||||
|             }; | ||||
|         })(); | ||||
| @@ -173,7 +194,7 @@ RED.notifications = (function() { | ||||
|  | ||||
|         n.update = (function() { | ||||
|             var nn = n; | ||||
|             return function(msg,options) { | ||||
|             return function(msg,newOptions) { | ||||
|                 if (typeof msg === "string") { | ||||
|                     if (!/<p>/i.test(msg)) { | ||||
|                         msg = "<p>"+msg+"</p>"; | ||||
| @@ -182,16 +203,31 @@ RED.notifications = (function() { | ||||
|                 } else { | ||||
|                     $(nn).empty().append(msg); | ||||
|                 } | ||||
|                 var timeout; | ||||
|                 if (typeof options === 'number') { | ||||
|                     timeout = options; | ||||
|                 } else if (options !== undefined) { | ||||
|                     if (!options.fixed) { | ||||
|                         timeout = options.timeout || 5000; | ||||
|                 var newTimeout; | ||||
|                 if (typeof newOptions === 'number') { | ||||
|                     newTimeout = newOptions; | ||||
|                     nn.options.timeout = newTimeout; | ||||
|                 } else if (newOptions !== undefined) { | ||||
|  | ||||
|                     if (!options.modal && newOptions.modal) { | ||||
|                         nn.options.modal = true; | ||||
|                         shade.show(); | ||||
|                     } else if (options.modal && newOptions.modal === false) { | ||||
|                         nn.options.modal = false; | ||||
|                         shade.hide(); | ||||
|                     } | ||||
|                     if (options.buttons) { | ||||
|  | ||||
|                     var newType = newOptions.hasOwnProperty('type')?newOptions.type:type; | ||||
|                     if (newType) { | ||||
|                         n.className = "red-ui-notification red-ui-notification-"+newType; | ||||
|                     } | ||||
|  | ||||
|                     if (!fixed || newOptions.fixed === false) { | ||||
|                         newTimeout = (newOptions.hasOwnProperty('timeout')?newOptions.timeout:timeout)||5000; | ||||
|                     } | ||||
|                     if (newOptions.buttons) { | ||||
|                         var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn) | ||||
|                         options.buttons.forEach(function(buttonDef) { | ||||
|                         newOptions.buttons.forEach(function(buttonDef) { | ||||
|                             var b = $('<button>').text(buttonDef.text).on("click", buttonDef.click).appendTo(buttonSet); | ||||
|                             if (buttonDef.id) { | ||||
|                                 b.attr('id',buttonDef.id); | ||||
| @@ -202,15 +238,22 @@ RED.notifications = (function() { | ||||
|                         }) | ||||
|                     } | ||||
|                 } | ||||
|                 if (timeout !== undefined && timeout > 0) { | ||||
|                 $(nn).off("click.red-ui-notification-close"); | ||||
|                 if (newTimeout !== undefined && newTimeout > 0) { | ||||
|                     window.clearTimeout(nn.timeoutid); | ||||
|                     nn.timeoutid = window.setTimeout(nn.close,timeout); | ||||
|                     nn.timeoutid = window.setTimeout(nn.close,newTimeout); | ||||
|                     setTimeout(function() { | ||||
|                         $(nn).on("click.red-ui-notification-close", function() { | ||||
|                             nn.close(); | ||||
|                             window.clearTimeout(nn.timeoutid); | ||||
|                         }); | ||||
|                     },50); | ||||
|                 } else { | ||||
|                     window.clearTimeout(nn.timeoutid); | ||||
|                 } | ||||
|                 if (nn.hidden) { | ||||
|                     nn.showNotification(); | ||||
|                 } else if (!options || !options.silent){ | ||||
|                 } else if (!newOptions || !newOptions.silent){ | ||||
|                     $(nn).addClass("red-ui-notification-shake-horizontal"); | ||||
|                     setTimeout(function() { | ||||
|                         $(nn).removeClass("red-ui-notification-shake-horizontal"); | ||||
| @@ -221,7 +264,7 @@ RED.notifications = (function() { | ||||
|         })(); | ||||
|  | ||||
|         if (!fixed) { | ||||
|             $(n).on("click", (function() { | ||||
|             $(n).on("click.red-ui-notification-close", (function() { | ||||
|                 var nn = n; | ||||
|                 return function() { | ||||
|                     nn.close(); | ||||
|   | ||||
| @@ -264,6 +264,8 @@ RED.palette.editor = (function() { | ||||
|                             var errMessage = set.err; | ||||
|                             if (set.err.message) { | ||||
|                                 errMessage = set.err.message; | ||||
|                             } else if (set.err.code) { | ||||
|                                 errMessage = set.err.code; | ||||
|                             } | ||||
|                             $("<li>").text(errMessage).appendTo(nodeEntry.errorList); | ||||
|                         } | ||||
| @@ -331,7 +333,10 @@ RED.palette.editor = (function() { | ||||
|                 nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow) | ||||
|                 nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').css('display', 'inline-block'); | ||||
|             } else if (loadedIndex.hasOwnProperty(module)) { | ||||
|                 if (semVerCompare(loadedIndex[module].version,moduleInfo.version) > 0) { | ||||
|                 if (updateAllowed && | ||||
|                     semVerCompare(loadedIndex[module].version,moduleInfo.version) > 0 && | ||||
|                     RED.utils.checkModuleAllowed(module,null,updateAllowList,updateDenyList) | ||||
|                 ) { | ||||
|                     nodeEntry.updateButton.show(); | ||||
|                     nodeEntry.updateButton.text(RED._('palette.editor.update',{version:loadedIndex[module].version})); | ||||
|                 } else { | ||||
| @@ -482,6 +487,9 @@ RED.palette.editor = (function() { | ||||
|  | ||||
|     var installAllowList = ['*']; | ||||
|     var installDenyList = []; | ||||
|     var updateAllowed = true; | ||||
|     var updateAllowList = ['*']; | ||||
|     var updateDenyList = []; | ||||
|  | ||||
|     function init() { | ||||
|         if (RED.settings.get('externalModules.palette.allowInstall', true) === false) { | ||||
| @@ -496,6 +504,17 @@ RED.palette.editor = (function() { | ||||
|         installAllowList = RED.utils.parseModuleList(installAllowList); | ||||
|         installDenyList = RED.utils.parseModuleList(installDenyList); | ||||
|  | ||||
|         var settingsUpdateAllowList = RED.settings.get("externalModules.palette.allowUpdateList") | ||||
|         var settingsUpdateDenyList = RED.settings.get("externalModules.palette.denyUpdateList") | ||||
|         if (settingsUpdateAllowList || settingsUpdateDenyList) { | ||||
|             updateAllowList = settingsUpdateAllowList; | ||||
|             updateDenyList = settingsUpdateDenyList; | ||||
|         } | ||||
|         updateAllowList = RED.utils.parseModuleList(updateAllowList); | ||||
|         updateDenyList = RED.utils.parseModuleList(updateDenyList); | ||||
|         updateAllowed = RED.settings.get("externalModules.palette.allowUpdate",true); | ||||
|  | ||||
|  | ||||
|         createSettingsPane(); | ||||
|  | ||||
|         RED.userSettings.add({ | ||||
|   | ||||
| @@ -224,14 +224,7 @@ RED.palette = (function() { | ||||
|  | ||||
|             var d = $('<div>',{class:"red-ui-palette-node"}).attr("data-palette-type",nt).data('category',rootCategory); | ||||
|  | ||||
|             var label = nt;///^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1]; | ||||
|             if (typeof def.paletteLabel !== "undefined") { | ||||
|                 try { | ||||
|                     label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||""; | ||||
|                 } catch(err) { | ||||
|                     console.log("Definition error: "+nt+".paletteLabel",err); | ||||
|                 } | ||||
|             } | ||||
|             var label = RED.utils.getPaletteLabel(nt, def);///^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1]; | ||||
|  | ||||
|             $('<div/>', { | ||||
|                 class: "red-ui-palette-label"+(((!def.align && def.inputs !== 0 && def.outputs === 0) || "right" === def.align) ? " red-ui-palette-label-right" : "") | ||||
| @@ -320,12 +313,12 @@ RED.palette = (function() { | ||||
|                     var paletteNode = getPaletteNode(nt); | ||||
|                     ui.originalPosition.left = paletteNode.offset().left; | ||||
|                     mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft(); | ||||
|                     mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop(); | ||||
|                     mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10; | ||||
|                     if (!groupTimer) { | ||||
|                         groupTimer = setTimeout(function() { | ||||
|                             mouseX /= RED.view.scale(); | ||||
|                             mouseY /= RED.view.scale(); | ||||
|                             var group = RED.view.getGroupAtPoint(mouseX,mouseY); | ||||
|                             var mx = mouseX / RED.view.scale(); | ||||
|                             var my = mouseY / RED.view.scale(); | ||||
|                             var group = RED.view.getGroupAtPoint(mx,my); | ||||
|                             if (group !== hoverGroup) { | ||||
|                                 if (hoverGroup) { | ||||
|                                     document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); | ||||
| @@ -357,23 +350,20 @@ RED.palette = (function() { | ||||
|                                     svgRect.width = 1; | ||||
|                                     svgRect.height = 1; | ||||
|                                     nodes = chartSVG.getIntersectionList(svgRect,chartSVG); | ||||
|                                     mouseX /= RED.view.scale(); | ||||
|                                     mouseY /= RED.view.scale(); | ||||
|                                 } else { | ||||
|                                     // Firefox doesn't do getIntersectionList and that | ||||
|                                     // makes us sad | ||||
|                                     mouseX /= RED.view.scale(); | ||||
|                                     mouseY /= RED.view.scale(); | ||||
|                                     nodes = RED.view.getLinksAtPoint(mouseX,mouseY); | ||||
|                                 } | ||||
|  | ||||
|                                 var mx = mouseX / RED.view.scale(); | ||||
|                                 var my = mouseY / RED.view.scale(); | ||||
|                                 for (var i=0;i<nodes.length;i++) { | ||||
|                                     var node = d3.select(nodes[i]); | ||||
|                                     if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) { | ||||
|                                         var length = nodes[i].getTotalLength(); | ||||
|                                         for (var j=0;j<length;j+=10) { | ||||
|                                             var p = nodes[i].getPointAtLength(j); | ||||
|                                             var d2 = ((p.x-mouseX)*(p.x-mouseX))+((p.y-mouseY)*(p.y-mouseY)); | ||||
|                                             var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my)); | ||||
|                                             if (d2 < 200 && d2 < bestDistance) { | ||||
|                                                 bestDistance = d2; | ||||
|                                                 bestLink = nodes[i]; | ||||
|   | ||||
| @@ -120,6 +120,7 @@ RED.projects.settings = (function() { | ||||
|             title: RED._('sidebar.project.editDescription'), | ||||
|             header: $('<span><i class="fa fa-book"></i> README.md</span>'), | ||||
|             value: activeProject.description, | ||||
|             stateId: "sidebar.project.editDescription", | ||||
|             complete: function(v) { | ||||
|                 container.empty(); | ||||
|                 var spinner = utils.addSpinnerOverlay(container); | ||||
|   | ||||
| @@ -1212,6 +1212,9 @@ RED.projects = (function() { | ||||
|                             } | ||||
|                         }).appendTo(row); | ||||
|  | ||||
|                         row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-open"></div>').hide().appendTo(container); | ||||
|                         $('<span style="display: flex; align-items: center;"><input style="padding:0; margin: 0 5px 0 0" checked type="checkbox" id="red-ui-projects-dialog-screen-clear-context"> <label for="red-ui-projects-dialog-screen-clear-context" style="padding:0; margin: 0"> <span data-i18n="projects.create.clearContext"></span></label></span>').appendTo(row).i18n(); | ||||
|  | ||||
|                         row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container); | ||||
|                         $('<label for="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.create.project-name")+'</label>').appendTo(row); | ||||
|  | ||||
| @@ -1501,7 +1504,8 @@ RED.projects = (function() { | ||||
|                                             }; | ||||
|                                         } | ||||
|                                     } else if (projectType === 'open') { | ||||
|                                         return switchProject(selectedProject.name,function(err,data) { | ||||
|                                         var clearContext = $("#red-ui-projects-dialog-screen-clear-context").prop("checked") | ||||
|                                         return switchProject(selectedProject.name, clearContext, function(err,data) { | ||||
|                                             if (err) { | ||||
|                                                 if (err.code !== 'credentials_load_failed') { | ||||
|                                                     console.log(RED._("projects.create.unexpected_error"),err) | ||||
| @@ -1595,7 +1599,7 @@ RED.projects = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function switchProject(name,done) { | ||||
|     function switchProject(name,clearContext,done) { | ||||
|         RED.deploy.setDeployInflight(true); | ||||
|         RED.projects.settings.switchProject(name); | ||||
|         sendRequest({ | ||||
| @@ -1614,7 +1618,7 @@ RED.projects = (function() { | ||||
|                     '*': done | ||||
|                 }, | ||||
|             } | ||||
|         },{active:true}).then(function() { | ||||
|         },{active:true, clearContext:clearContext}).then(function() { | ||||
|             dialog.dialog( "close" ); | ||||
|             RED.events.emit("project:change", {name:name}); | ||||
|         }).always(function() { | ||||
| @@ -1687,7 +1691,7 @@ RED.projects = (function() { | ||||
|             dialogHeight = 590 - (750 - winHeight); | ||||
|         } | ||||
|         $(".red-ui-projects-dialog-box").height(dialogHeight); | ||||
|         $(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 180); | ||||
|         $(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 210); | ||||
|         dialog.dialog('option','title',screen.title||""); | ||||
|         dialog.dialog("open"); | ||||
|     } | ||||
| @@ -2387,6 +2391,7 @@ RED.projects = (function() { | ||||
|     return { | ||||
|         init: init, | ||||
|         showStartup: function() { | ||||
|             console.warn("showStartup") | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|   | ||||
| @@ -22,8 +22,11 @@ RED.search = (function() { | ||||
|     var selected = -1; | ||||
|     var visible = false; | ||||
|  | ||||
|     var searchHistory = []; | ||||
|     var index = {}; | ||||
|     var currentResults = []; | ||||
|     var activeResults = []; | ||||
|     var currentIndex = 0; | ||||
|     var previousActiveElement; | ||||
|  | ||||
|     function indexProperty(node,label,property) { | ||||
| @@ -52,10 +55,22 @@ RED.search = (function() { | ||||
|         } | ||||
|         l = l||n.label||n.name||n.id||""; | ||||
|  | ||||
|  | ||||
|         var properties = ['id','type','name','label','info']; | ||||
|         if (n._def && n._def.defaults) { | ||||
|             properties = properties.concat(Object.keys(n._def.defaults)); | ||||
|         const node_def = n && n._def; | ||||
|         if (node_def) { | ||||
|             if (node_def.defaults) { | ||||
|                 properties = properties.concat(Object.keys(node_def.defaults)); | ||||
|             } | ||||
|             if (n.type !== "group" && node_def.paletteLabel && node_def.paletteLabel !== node_def.type) { | ||||
|                 try { | ||||
|                     const label = ("" + (typeof node_def.paletteLabel === "function" ? node_def.paletteLabel.call(node_def) : node_def.paletteLabel)).toLowerCase(); | ||||
|                     if(label && label !== (""+node_def.type).toLowerCase()) { | ||||
|                         indexProperty(n, l, label); | ||||
|                     } | ||||
|                 } catch(err) { | ||||
|                     console.warn(`error indexing ${l}`, err); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for (var i=0;i<properties.length;i++) { | ||||
|             if (n.hasOwnProperty(properties[i])) { | ||||
| @@ -93,7 +108,6 @@ RED.search = (function() { | ||||
|  | ||||
|     function search(val) { | ||||
|         var results = []; | ||||
|         var keys = []; | ||||
|         var typeFilter; | ||||
|         var m = /(?:^| )type:([^ ]+)/.exec(val); | ||||
|         if (m) { | ||||
| @@ -105,19 +119,26 @@ RED.search = (function() { | ||||
|         val = extractFlag(val,"unused",flags); | ||||
|         val = extractFlag(val,"config",flags); | ||||
|         val = extractFlag(val,"subflow",flags); | ||||
|         // uses:<node-id> | ||||
|         val = extractValue(val,"uses",flags); | ||||
|  | ||||
|         var hasFlags = Object.keys(flags).length > 0; | ||||
|  | ||||
|         val = extractFlag(val,"hidden",flags); | ||||
|         val = extractFlag(val,"modified",flags); | ||||
|         val = extractValue(val,"flow",flags);// flow:active or flow:<flow-id> | ||||
|         val = extractValue(val,"uses",flags);// uses:<node-id> | ||||
|         val = val.trim(); | ||||
|  | ||||
|         var hasFlags = Object.keys(flags).length > 0; | ||||
|         if (flags.flow && flags.flow.indexOf("current") >= 0) { | ||||
|             let idx = flags.flow.indexOf("current"); | ||||
|             flags.flow[idx] = RED.workspaces.active();//convert active to flow ID | ||||
|         } | ||||
|         if (flags.flow && flags.flow.length) { | ||||
|             flags.flow = [ ...new Set(flags.flow) ]; //deduplicate | ||||
|         } | ||||
|         if (val.length > 0 || typeFilter || hasFlags) { | ||||
|             val = val.toLowerCase(); | ||||
|             var i; | ||||
|             var j; | ||||
|             var list = []; | ||||
|             var nodes = {}; | ||||
|             let keys = []; | ||||
|             if (flags.uses) { | ||||
|                 keys = flags.uses; | ||||
|             } else { | ||||
| @@ -127,7 +148,7 @@ RED.search = (function() { | ||||
|                 var key = keys[i]; | ||||
|                 var kpos = keys[i].indexOf(val); | ||||
|                 if (kpos > -1) { | ||||
|                     var ids = Object.keys(index[key]); | ||||
|                     var ids = Object.keys(index[key]||{}); | ||||
|                     for (j=0;j<ids.length;j++) { | ||||
|                         var node = index[key][ids[j]]; | ||||
|                         var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group'; | ||||
| @@ -150,7 +171,20 @@ RED.search = (function() { | ||||
|                                 continue; | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if (flags.hasOwnProperty("modified")) { | ||||
|                             if (!node.node.changed && !node.node.moved) { | ||||
|                                 continue; | ||||
|                             } | ||||
|                         } | ||||
|                         if (flags.hasOwnProperty("hidden")) { | ||||
|                             // Only tabs can be hidden | ||||
|                             if (node.node.type !== 'tab') { | ||||
|                                 continue | ||||
|                             } | ||||
|                             if (!RED.workspaces.isHidden(node.node.id)) { | ||||
|                                 continue | ||||
|                             } | ||||
|                         } | ||||
|                         if (flags.hasOwnProperty("unused")) { | ||||
|                             var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) || | ||||
|                                            (isConfigNode && node.node.users.length === 0) | ||||
| @@ -158,6 +192,11 @@ RED.search = (function() { | ||||
|                                 continue; | ||||
|                             } | ||||
|                         } | ||||
|                         if (flags.hasOwnProperty("flow")) { | ||||
|                             if (flags.flow.indexOf(node.node.z || node.node.id) < 0) { | ||||
|                                 continue; | ||||
|                             } | ||||
|                         } | ||||
|                         if (!typeFilter || node.node.type === typeFilter) { | ||||
|                             nodes[node.node.id] = nodes[node.node.id] = { | ||||
|                                 node: node.node, | ||||
| @@ -196,6 +235,20 @@ RED.search = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function populateSearchHistory() { | ||||
|         if (searchHistory.length > 0) { | ||||
|             searchResults.editableList('addItem',{ | ||||
|                 historyHeader: true | ||||
|             }); | ||||
|             searchHistory.forEach(function(entry) { | ||||
|                 searchResults.editableList('addItem',{ | ||||
|                     history: true, | ||||
|                     value: entry | ||||
|                 }); | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     function createDialog() { | ||||
|         dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#red-ui-main-container"); | ||||
|         var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog); | ||||
| @@ -204,9 +257,14 @@ RED.search = (function() { | ||||
|             change: function() { | ||||
|                 searchResults.editableList('empty'); | ||||
|                 selected = -1; | ||||
|                 currentResults = search($(this).val()); | ||||
|                 var value = $(this).val(); | ||||
|                 if (value === "") { | ||||
|                     populateSearchHistory(); | ||||
|                     return; | ||||
|                 } | ||||
|                 currentResults = search(value); | ||||
|                 if (currentResults.length > 0) { | ||||
|                     for (i=0;i<Math.min(currentResults.length,25);i++) { | ||||
|                     for (let i=0;i<Math.min(currentResults.length,25);i++) { | ||||
|                         searchResults.editableList('addItem',currentResults[i]) | ||||
|                     } | ||||
|                     if (currentResults.length > 25) { | ||||
| @@ -220,9 +278,8 @@ RED.search = (function() { | ||||
|                 } else { | ||||
|                     searchResults.editableList('addItem',{}); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|             }, | ||||
|             options: getSearchOptions() | ||||
|         }); | ||||
|         var copySearchContainer = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-caret-right"></button>').appendTo(searchDiv).on('click', function(evt) { | ||||
|             evt.preventDefault(); | ||||
| @@ -276,9 +333,15 @@ RED.search = (function() { | ||||
|                                 }) | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                     } if ($(children[selected]).hasClass("red-ui-search-history")) { | ||||
|                         var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data'); | ||||
|                         if (object) { | ||||
|                             searchInput.searchBox('value',object.value) | ||||
|                         } | ||||
|                     } else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) { | ||||
|                         if (currentResults.length > 0) { | ||||
|                             reveal(currentResults[Math.max(0,selected)].node); | ||||
|                             currentIndex = Math.max(0,selected); | ||||
|                             reveal(currentResults[currentIndex].node); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -292,7 +355,32 @@ RED.search = (function() { | ||||
|             addItem: function(container,i,object) { | ||||
|                 var node = object.node; | ||||
|                 var div; | ||||
|                 if (object.more) { | ||||
|                 if (object.historyHeader) { | ||||
|                     container.parent().addClass("red-ui-search-historyHeader") | ||||
|                     $('<div>',{class:"red-ui-search-empty"}).text(RED._("search.history")).appendTo(container); | ||||
|                     $('<button type="button" class="red-ui-button red-ui-button-small"></button>').text(RED._("search.clear")).appendTo(container).on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         searchHistory = []; | ||||
|                         searchResults.editableList('empty'); | ||||
|                     }); | ||||
|                 } else if (object.history) { | ||||
|                     container.parent().addClass("red-ui-search-history") | ||||
|                     div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container); | ||||
|                     div.text(object.value); | ||||
|                     div.on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         searchInput.searchBox('value',object.value) | ||||
|                         searchInput.focus(); | ||||
|                     }) | ||||
|                     $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></button>').appendTo(container).on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         var index = searchHistory.indexOf(object.value); | ||||
|                         searchHistory.splice(index,1); | ||||
|                         searchResults.editableList('removeItem', object); | ||||
|                     }); | ||||
|  | ||||
|  | ||||
|                 } else if (object.more) { | ||||
|                     container.parent().addClass("red-ui-search-more") | ||||
|                     div = $('<a>',{href:'#',class:"red-ui-search-result red-ui-search-empty"}).appendTo(container); | ||||
|                     div.text(RED._("palette.editor.more",{count:object.more.results.length-object.more.start})); | ||||
| @@ -337,6 +425,7 @@ RED.search = (function() { | ||||
|  | ||||
|                     div.on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         currentIndex = i; | ||||
|                         reveal(node); | ||||
|                     }); | ||||
|                 } | ||||
| @@ -347,12 +436,64 @@ RED.search = (function() { | ||||
|     } | ||||
|  | ||||
|     function reveal(node) { | ||||
|         hide(); | ||||
|         var searchVal = searchInput.val(); | ||||
|         var existingIndex = searchHistory.indexOf(searchVal); | ||||
|         if (existingIndex > -1) { | ||||
|             searchHistory.splice(existingIndex,1); | ||||
|         } | ||||
|         searchHistory.unshift(searchVal); | ||||
|         $("#red-ui-view-searchtools-search").data("term", searchVal); | ||||
|         activeResults = Object.assign([], currentResults); | ||||
|         hide(null, activeResults.length > 0); | ||||
|         RED.view.reveal(node.id); | ||||
|     } | ||||
|  | ||||
|     function revealPrev() { | ||||
|         if (disabled) { | ||||
|             updateSearchToolbar(); | ||||
|             return; | ||||
|         } | ||||
|         if (!searchResults || !activeResults.length) { | ||||
|             show(); | ||||
|             return; | ||||
|         } | ||||
|         if (currentIndex > 0) { | ||||
|             currentIndex--; | ||||
|         } else { | ||||
|             currentIndex = activeResults.length - 1; | ||||
|         } | ||||
|         const n = activeResults[currentIndex]; | ||||
|         if (n && n.node && n.node.id) { | ||||
|             RED.view.reveal(n.node.id); | ||||
|             $("#red-ui-view-searchtools-prev").trigger("focus"); | ||||
|         } | ||||
|         updateSearchToolbar(); | ||||
|     } | ||||
|     function revealNext() { | ||||
|         if (disabled) { | ||||
|             updateSearchToolbar(); | ||||
|             return; | ||||
|         } | ||||
|         if (!searchResults || !activeResults.length) { | ||||
|             show(); | ||||
|             return; | ||||
|         } | ||||
|         if (currentIndex < activeResults.length - 1) { | ||||
|             currentIndex++ | ||||
|         } else { | ||||
|             currentIndex = 0; | ||||
|         } | ||||
|         const n = activeResults[currentIndex]; | ||||
|         if (n && n.node && n.node.id) { | ||||
|             RED.view.reveal(n.node.id); | ||||
|             $("#red-ui-view-searchtools-next").trigger("focus"); | ||||
|         } | ||||
|         updateSearchToolbar(); | ||||
|     } | ||||
|  | ||||
|     function show(v) { | ||||
|         if (disabled) { | ||||
|             updateSearchToolbar(); | ||||
|             return; | ||||
|         } | ||||
|         if (!visible) { | ||||
| @@ -365,16 +506,21 @@ RED.search = (function() { | ||||
|  | ||||
|             if (dialog === null) { | ||||
|                 createDialog(); | ||||
|             } else { | ||||
|                 searchResults.editableList('empty'); | ||||
|             } | ||||
|             dialog.slideDown(300); | ||||
|             searchInput.searchBox('value',v) | ||||
|             if (!v || v === "") { | ||||
|                 populateSearchHistory(); | ||||
|             } | ||||
|             RED.events.emit("search:open"); | ||||
|             visible = true; | ||||
|         } | ||||
|         searchInput.trigger("focus"); | ||||
|     } | ||||
|  | ||||
|     function hide() { | ||||
|     function hide(el, keepSearchToolbar) { | ||||
|         if (visible) { | ||||
|             visible = false; | ||||
|             $("#red-ui-header-shade").hide(); | ||||
| @@ -388,13 +534,37 @@ RED.search = (function() { | ||||
|                 }); | ||||
|             } | ||||
|             RED.events.emit("search:close"); | ||||
|             if (previousActiveElement) { | ||||
|             if (previousActiveElement && (!keepSearchToolbar || !activeResults.length)) { | ||||
|                 $(previousActiveElement).trigger("focus"); | ||||
|                 previousActiveElement = null; | ||||
|             } | ||||
|             previousActiveElement = null; | ||||
|         }  | ||||
|         if(!keepSearchToolbar) { | ||||
|             clearActiveSearch(); | ||||
|         } | ||||
|         updateSearchToolbar(); | ||||
|         if(keepSearchToolbar && activeResults.length) { | ||||
|             $("#red-ui-view-searchtools-next").trigger("focus"); | ||||
|         } | ||||
|     } | ||||
|     function updateSearchToolbar() { | ||||
|         if (!disabled && currentIndex >= 0 && activeResults && activeResults.length) { | ||||
|             let term = $("#red-ui-view-searchtools-search").data("term") || ""; | ||||
|             if (term.length > 16) { | ||||
|                 term = term.substring(0, 12) + "..." | ||||
|             } | ||||
|             const i18nSearchCounterData = { | ||||
|                 term: term, | ||||
|                 result: (currentIndex + 1), | ||||
|                 count: activeResults.length | ||||
|             } | ||||
|             $("#red-ui-view-searchtools-counter").text(RED._('actions.search-counter', i18nSearchCounterData)); | ||||
|             $("#view-search-tools > :not(:first-child)").show(); //show other tools | ||||
|         } else { | ||||
|             clearActiveSearch(); | ||||
|             $("#view-search-tools > :not(:first-child)").hide(); //hide all but search button | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function clearIndex() { | ||||
|         index = {}; | ||||
|     } | ||||
| @@ -416,9 +586,29 @@ RED.search = (function() { | ||||
|         addItemToIndex(item); | ||||
|     } | ||||
|  | ||||
|     function clearActiveSearch() { | ||||
|         activeResults = []; | ||||
|         currentIndex = 0; | ||||
|         $("#red-ui-view-searchtools-search").data("term", ""); | ||||
|     } | ||||
|  | ||||
|     function getSearchOptions() { | ||||
|         return [ | ||||
|             {label:RED._("search.options.configNodes"), value:"is:config"}, | ||||
|             {label:RED._("search.options.unusedConfigNodes"), value:"is:config is:unused"}, | ||||
|             {label:RED._("search.options.modifiedNodes"), value:"is:modified"}, | ||||
|             {label:RED._("search.options.invalidNodes"), value: "is:invalid"}, | ||||
|             {label:RED._("search.options.uknownNodes"), value: "type:unknown"}, | ||||
|             {label:RED._("search.options.unusedSubflows"), value:"is:subflow is:unused"}, | ||||
|             {label:RED._("search.options.hiddenFlows"), value:"is:hidden"}, | ||||
|             {label:RED._("search.options.thisFlow"), value:"flow:current"}, | ||||
|         ] | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         RED.actions.add("core:search",show); | ||||
|         RED.actions.add("core:search-previous",revealPrev); | ||||
|         RED.actions.add("core:search-next",revealNext); | ||||
|  | ||||
|         RED.events.on("editor:open",function() { disabled = true; }); | ||||
|         RED.events.on("editor:close",function() { disabled = false; }); | ||||
| @@ -429,11 +619,21 @@ RED.search = (function() { | ||||
|  | ||||
|         RED.keyboard.add("red-ui-search","escape",hide); | ||||
|  | ||||
|         RED.keyboard.add("view-search-tools","escape",function() { | ||||
|             clearActiveSearch(); | ||||
|             updateSearchToolbar(); | ||||
|         }); | ||||
|  | ||||
|         $("#red-ui-header-shade").on('mousedown',hide); | ||||
|         $("#red-ui-editor-shade").on('mousedown',hide); | ||||
|         $("#red-ui-palette-shade").on('mousedown',hide); | ||||
|         $("#red-ui-sidebar-shade").on('mousedown',hide); | ||||
|  | ||||
|         $("#red-ui-view-searchtools-close").on("click", function close() { | ||||
|             clearActiveSearch();             | ||||
|             updateSearchToolbar(); | ||||
|         }); | ||||
|         $("#red-ui-view-searchtools-close").trigger("click"); | ||||
|  | ||||
|         RED.events.on("workspace:clear", clearIndex); | ||||
|  | ||||
| @@ -459,7 +659,8 @@ RED.search = (function() { | ||||
|         init: init, | ||||
|         show: show, | ||||
|         hide: hide, | ||||
|         search: search | ||||
|         search: search, | ||||
|         getSearchOptions: getSearchOptions | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -19,6 +19,15 @@ RED.sidebar = (function() { | ||||
|     var sidebar_tabs; | ||||
|     var knownTabs = {}; | ||||
|  | ||||
|     // We store the current sidebar tab id in localStorage as 'last-sidebar-tab' | ||||
|     // This is restored when the editor is reloaded. | ||||
|     // We use sidebar_tabs.onchange to update localStorage. However that will | ||||
|     // also get triggered when the first tab gets added to the tabs - typically | ||||
|     // the 'info' tab. So we use the following variable to store the retrieved | ||||
|     // value from localStorage before we start adding the actual tabs | ||||
|     var lastSessionSelectedTab = null; | ||||
|  | ||||
|  | ||||
|     function addTab(title,content,closeable,visible) { | ||||
|         var options; | ||||
|         if (typeof title === "string") { | ||||
| @@ -194,16 +203,16 @@ RED.sidebar = (function() { | ||||
|         RED.events.emit("sidebar:resize"); | ||||
|     } | ||||
|  | ||||
|     function showSidebar(id) { | ||||
|     function showSidebar(id, skipShowSidebar) { | ||||
|         if (id === ":first") { | ||||
|             id = RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0] | ||||
|             id = lastSessionSelectedTab || RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0] | ||||
|         } | ||||
|         if (id) { | ||||
|             if (!containsTab(id)) { | ||||
|             if (!containsTab(id) && knownTabs[id]) { | ||||
|                 sidebar_tabs.addTab(knownTabs[id]); | ||||
|             } | ||||
|             sidebar_tabs.activateTab(id); | ||||
|             if (!RED.menu.isSelected("menu-item-sidebar")) { | ||||
|             if (!skipShowSidebar && !RED.menu.isSelected("menu-item-sidebar")) { | ||||
|                 RED.menu.setSelected("menu-item-sidebar",true); | ||||
|             } | ||||
|         } | ||||
| @@ -227,6 +236,7 @@ RED.sidebar = (function() { | ||||
|                 if (tab.toolbar) { | ||||
|                     $(tab.toolbar).show(); | ||||
|                 } | ||||
|                 RED.settings.setLocal("last-sidebar-tab", tab.id) | ||||
|             }, | ||||
|             onremove: function(tab) { | ||||
|                 $(tab.wrapper).hide(); | ||||
| @@ -255,7 +265,9 @@ RED.sidebar = (function() { | ||||
|             } | ||||
|         }); | ||||
|         RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar"); | ||||
|         showSidebar(); | ||||
|  | ||||
|         lastSessionSelectedTab = RED.settings.getLocal("last-sidebar-tab") | ||||
|  | ||||
|         RED.sidebar.info.init(); | ||||
|         RED.sidebar.help.init(); | ||||
|         RED.sidebar.config.init(); | ||||
|   | ||||
| @@ -27,5 +27,8 @@ RED.state = { | ||||
|     PANNING: 10, | ||||
|     SELECTING_NODE: 11, | ||||
|     GROUP_DRAGGING: 12, | ||||
|     GROUP_RESIZE: 13 | ||||
|     GROUP_RESIZE: 13, | ||||
|     DETACHED_DRAGGING: 14, | ||||
|     SLICING: 15, | ||||
|     SLICING_JUNCTION: 16 | ||||
| } | ||||
|   | ||||
| @@ -31,6 +31,7 @@ RED.statusBar = (function() { | ||||
|     function addWidget(options) { | ||||
|         widgets[options.id] = options; | ||||
|         var el = $('<span class="red-ui-statusbar-widget"></span>'); | ||||
|         el.prop('id', options.id); | ||||
|         options.element.appendTo(el); | ||||
|         if (options.align === 'left') { | ||||
|             leftBucket.append(el); | ||||
|   | ||||
| @@ -16,8 +16,6 @@ | ||||
|  | ||||
| RED.subflow = (function() { | ||||
|  | ||||
|     var currentLocale = "en-US"; | ||||
|  | ||||
|     var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+ | ||||
| @@ -37,7 +35,7 @@ RED.subflow = (function() { | ||||
|         '<div id="subflow-env-tabs-content">'+ | ||||
|             '<div id="subflow-env-tab-edit">'+ | ||||
|                 '<div class="form-row node-input-env-container-row" id="subflow-input-edit-ui">'+ | ||||
|                     '<ol class="red-ui-editor-subflow-env-list" id="node-input-env-container"></ol>'+ | ||||
|                     '<ol id="node-input-env-container"></ol>'+ | ||||
|                     '<div class="node-input-env-locales-row"><i class="fa fa-language"></i> <select id="subflow-input-env-locale"></select></div>'+ | ||||
|                 '</div>'+ | ||||
|             '</div>'+ | ||||
| @@ -47,37 +45,6 @@ RED.subflow = (function() { | ||||
|         '</div>'+ | ||||
|         '</script>'; | ||||
|  | ||||
|     var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+ | ||||
|             '<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+ | ||||
|         '</div>'+ | ||||
|     '</form>'; | ||||
|  | ||||
|     function findAvailableSubflowIOPosition(subflow,isInput) { | ||||
|         var pos = {x:50,y:30}; | ||||
|         if (!isInput) { | ||||
| @@ -909,7 +876,6 @@ RED.subflow = (function() { | ||||
|      * Create interface for controlling env var UI definition | ||||
|      */ | ||||
|     function buildEnvControl(envList,node) { | ||||
|  | ||||
|         var tabs = RED.tabs.create({ | ||||
|             id: "subflow-env-tabs", | ||||
|             onchange: function(tab) { | ||||
| @@ -950,588 +916,11 @@ RED.subflow = (function() { | ||||
|         locales.val(locale); | ||||
|  | ||||
|         locales.on("change", function() { | ||||
|             currentLocale = $(this).val(); | ||||
|             var items = $("#node-input-env-container").editableList("items"); | ||||
|             items.each(function (i, item) { | ||||
|                 var entry = $(this).data('data'); | ||||
|                 var labelField = entry.ui.labelField; | ||||
|                 labelField.val(lookupLabel(entry.ui.label, "", currentLocale)); | ||||
|                 if (labelField.timeout) { | ||||
|                     clearTimeout(labelField.timeout); | ||||
|                     delete labelField.timeout; | ||||
|                 } | ||||
|                 labelField.addClass("input-updated"); | ||||
|                 labelField.timeout = setTimeout(function() { | ||||
|                     delete labelField.timeout | ||||
|                     labelField.removeClass("input-updated"); | ||||
|                 },3000); | ||||
|             }); | ||||
|             RED.editor.envVarList.setLocale($(this).val(), $("#node-input-env-container")); | ||||
|         }); | ||||
|         RED.editor.envVarList.setLocale(locale); | ||||
|     } | ||||
|  | ||||
|     var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env']; | ||||
|     var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred']; | ||||
|  | ||||
|     /** | ||||
|      * Create env var edit interface | ||||
|      * @param container - container | ||||
|      * @param node - subflow node | ||||
|      */ | ||||
|     function buildPropertiesList(envContainer, node) { | ||||
|  | ||||
|         var isTemplateNode = (node.type === "subflow"); | ||||
|  | ||||
|         if (isTemplateNode) { | ||||
|             buildEnvControl(envContainer, node); | ||||
|         } | ||||
|         envContainer | ||||
|             .css({ | ||||
|                 'min-height':'150px', | ||||
|                 'min-width':'450px' | ||||
|             }) | ||||
|             .editableList({ | ||||
|                 header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined, | ||||
|                 addItem: function(container, i, opt) { | ||||
|                     // If this is an instance node, these are properties unique to | ||||
|                     // this instance - ie opt.parent will not be defined. | ||||
|  | ||||
|                     if (isTemplateNode) { | ||||
|                         container.addClass("red-ui-editor-subflow-env-editable") | ||||
|                     } | ||||
|  | ||||
|                     var envRow = $('<div/>').appendTo(container); | ||||
|                     var nameField = null; | ||||
|                     var valueField = null; | ||||
|  | ||||
|                     nameField = $('<input/>', { | ||||
|                         class: "node-input-env-name", | ||||
|                         type: "text", | ||||
|                         placeholder: RED._("common.label.name") | ||||
|                     }).attr("autocomplete","disable").appendTo(envRow).val(opt.name); | ||||
|                     valueField = $('<input/>',{ | ||||
|                         style: "width:100%", | ||||
|                         class: "node-input-env-value", | ||||
|                         type: "text", | ||||
|                     }).attr("autocomplete","disable").appendTo(envRow) | ||||
|                     valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED}); | ||||
|                     valueField.typedInput('type', opt.type); | ||||
|                     if (opt.type === "cred") { | ||||
|                         if (opt.value) { | ||||
|                             valueField.typedInput('value', opt.value); | ||||
|                         } else if (node.credentials && node.credentials[opt.name]) { | ||||
|                             valueField.typedInput('value', node.credentials[opt.name]); | ||||
|                         } else if (node.credentials && node.credentials['has_'+opt.name]) { | ||||
|                             valueField.typedInput('value', "__PWRD__"); | ||||
|                         } else { | ||||
|                             valueField.typedInput('value', ""); | ||||
|                         } | ||||
|                     } else { | ||||
|                         valueField.typedInput('value', opt.value); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     opt.nameField = nameField; | ||||
|                     opt.valueField = valueField; | ||||
|  | ||||
|                     var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow); | ||||
|                     $('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton); | ||||
|                     var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove")); | ||||
|                     actionButton.on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         removeTip.close(); | ||||
|                         container.parent().addClass("red-ui-editableList-item-deleting") | ||||
|                         container.fadeOut(300, function() { | ||||
|                             envContainer.editableList('removeItem',opt); | ||||
|                         }); | ||||
|                     }); | ||||
|  | ||||
|                     if (isTemplateNode) { | ||||
|                         // Add the UI customisation row | ||||
|                         // if `opt.ui` does not exist, then apply defaults. If these | ||||
|                         // defaults do not change then they will get stripped off | ||||
|                         // before saving. | ||||
|                         if (opt.type === 'cred') { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "", | ||||
|                                 type: "cred" | ||||
|                             } | ||||
|                             opt.ui.type = "cred"; | ||||
|                         } else { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "", | ||||
|                                 type: "input", | ||||
|                                 opts: {types:DEFAULT_ENV_TYPE_LIST} | ||||
|                             } | ||||
|                         } | ||||
|                         opt.ui.label = opt.ui.label || {}; | ||||
|                         opt.ui.type = opt.ui.type || "input"; | ||||
|  | ||||
|                         var uiRow = $('<div/>').appendTo(container).hide(); | ||||
|                         // save current info for reverting on cancel | ||||
|                         // var copy = $.extend(true, {}, ui); | ||||
|  | ||||
|                          $('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) { | ||||
|                             evt.preventDefault(); | ||||
|                             if ($(this).hasClass('expanded')) { | ||||
|                                 uiRow.slideUp(); | ||||
|                                 $(this).removeClass('expanded'); | ||||
|                             } else { | ||||
|                                 uiRow.slideDown(); | ||||
|                                 $(this).addClass('expanded'); | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         buildEnvEditRow(uiRow, opt.ui, nameField, valueField); | ||||
|                         nameField.trigger('change'); | ||||
|                     } | ||||
|                 }, | ||||
|                 sortable: ".red-ui-editableList-item-handle", | ||||
|                 removable: false | ||||
|             }); | ||||
|         var parentEnv = {}; | ||||
|         var envList = []; | ||||
|         if (/^subflow:/.test(node.type)) { | ||||
|             var subflowDef = RED.nodes.subflow(node.type.substring(8)); | ||||
|             if (subflowDef.env) { | ||||
|                 subflowDef.env.forEach(function(env) { | ||||
|                     var item = { | ||||
|                         name:env.name, | ||||
|                         parent: { | ||||
|                             type: env.type, | ||||
|                             value: env.value, | ||||
|                             ui: env.ui | ||||
|                         } | ||||
|                     } | ||||
|                     envList.push(item); | ||||
|                     parentEnv[env.name] = item; | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (node.env) { | ||||
|             for (var i = 0; i < node.env.length; i++) { | ||||
|                 var env = node.env[i]; | ||||
|                 if (parentEnv.hasOwnProperty(env.name)) { | ||||
|                     parentEnv[env.name].type = env.type; | ||||
|                     parentEnv[env.name].value = env.value; | ||||
|                 } else { | ||||
|                     envList.push({ | ||||
|                         name: env.name, | ||||
|                         type: env.type, | ||||
|                         value: env.value, | ||||
|                         ui: env.ui | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         envList.forEach(function(env) { | ||||
|             if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') { | ||||
|                 return; | ||||
|             } | ||||
|             if (!isTemplateNode && env.parent) { | ||||
|                 return; | ||||
|             } | ||||
|             envContainer.editableList('addItem', JSON.parse(JSON.stringify(env))); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create UI edit interface for environment variable | ||||
|      * @param container - container | ||||
|      * @param env - env var definition | ||||
|      * @param nameField - name field of env var | ||||
|      * @param valueField - value field of env var | ||||
|      */ | ||||
|      function buildEnvEditRow(container, ui, nameField, valueField) { | ||||
|          container.addClass("red-ui-editor-subflow-env-ui-row") | ||||
|          var topRow = $('<div></div>').appendTo(container); | ||||
|          $('<div></div>').appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.icon")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.label")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.inputType")).appendTo(topRow); | ||||
|  | ||||
|          var row = $('<div></div>').appendTo(container); | ||||
|          $('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row); | ||||
|          var typeOptions = { | ||||
|              'input': {types:DEFAULT_ENV_TYPE_LIST}, | ||||
|              'select': {opts:[]}, | ||||
|              'spinner': {}, | ||||
|              'cred': {} | ||||
|          }; | ||||
|          if (ui.opts) { | ||||
|              typeOptions[ui.type] = ui.opts; | ||||
|          } else { | ||||
|              // Pick up the default values if not otherwise provided | ||||
|              ui.opts = typeOptions[ui.type]; | ||||
|          } | ||||
|          var iconCell = $('<div></div>').appendTo(row); | ||||
|  | ||||
|          var iconButton = $('<a href="#"></a>').appendTo(iconCell); | ||||
|          iconButton.on("click", function(evt) { | ||||
|              evt.preventDefault(); | ||||
|              var icon = ui.icon || ""; | ||||
|              var iconPath = (icon ? RED.utils.separateIconPath(icon) : {}); | ||||
|              RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) { | ||||
|                  iconButton.empty(); | ||||
|                  var path = newIcon || ""; | ||||
|                  var newPath = RED.utils.separateIconPath(path); | ||||
|                  if (newPath) { | ||||
|                      $('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton); | ||||
|                  } | ||||
|                  ui.icon = path; | ||||
|              }); | ||||
|          }) | ||||
|  | ||||
|          if (ui.icon) { | ||||
|              var newPath = RED.utils.separateIconPath(ui.icon); | ||||
|              $('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton); | ||||
|          } | ||||
|  | ||||
|          var labelCell = $('<div></div>').appendTo(row); | ||||
|  | ||||
|          var label = ui.label && ui.label[currentLocale] || ""; | ||||
|          var labelInput = $('<input type="text">').val(label).appendTo(labelCell); | ||||
|          ui.labelField = labelInput; | ||||
|          labelInput.on('change', function(evt) { | ||||
|              ui.label = ui.label || {}; | ||||
|              var val = $(this).val().trim(); | ||||
|              if (val === "") { | ||||
|                  delete ui.label[currentLocale]; | ||||
|              } else { | ||||
|                  ui.label[currentLocale] = val; | ||||
|              } | ||||
|          }) | ||||
|          var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell); | ||||
|          RED.popover.tooltip(labelIcon,function() { | ||||
|              var langs = Object.keys(ui.label); | ||||
|              var content = $("<div>"); | ||||
|              if (langs.indexOf(currentLocale) === -1) { | ||||
|                  langs.push(currentLocale); | ||||
|                  langs.sort(); | ||||
|              } | ||||
|              langs.forEach(function(l) { | ||||
|                  var row = $('<div>').appendTo(content); | ||||
|                  $('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row); | ||||
|                  $('<span>').text(ui.label[l]||"").appendTo(row); | ||||
|              }); | ||||
|              return content; | ||||
|          }) | ||||
|  | ||||
|          nameField.on('change',function(evt) { | ||||
|             labelInput.attr("placeholder",$(this).val()) | ||||
|         }); | ||||
|  | ||||
|         var inputCell = $('<div></div>').appendTo(row); | ||||
|         var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell); | ||||
|         if (ui.type === "input") { | ||||
|             inputCellInput.val(ui.opts.types.join(",")); | ||||
|         } | ||||
|         var checkbox; | ||||
|         var selectBox; | ||||
|  | ||||
|         inputCellInput.typedInput({ | ||||
|             types: [ | ||||
|                 { | ||||
|                     value:"input", | ||||
|                     label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[ | ||||
|                         {value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"}, | ||||
|                         {value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"}, | ||||
|                         {value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"}, | ||||
|                         {value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"}, | ||||
|                         {value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"}, | ||||
|                         {value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"}, | ||||
|                         {value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"} | ||||
|                     ], | ||||
|                     default: DEFAULT_ENV_TYPE_LIST, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container); | ||||
|  | ||||
|                         var input = $('<div class="placeholder-input">').appendTo(innerContainer); | ||||
|                         $('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input); | ||||
|                         if (value.length) { | ||||
|                             value.forEach(function(v) { | ||||
|                                 if (!/^fa /.test(v.icon)) { | ||||
|                                     $('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input); | ||||
|                                 } else { | ||||
|                                     var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input); | ||||
|                                     $("<i>",{class: v.icon}).appendTo(s); | ||||
|                                 } | ||||
|                             }) | ||||
|                         } else { | ||||
|                             $('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input); | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value: "cred", | ||||
|                     label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({ | ||||
|                             "border-top-right-radius": "4px", | ||||
|                             "border-bottom-right-radius": "4px" | ||||
|                         }).appendTo(container); | ||||
|                         $('<div class="placeholder-input">').html("••••••••").appendTo(innerContainer); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"select", | ||||
|                     label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding","0"); | ||||
|  | ||||
|                         selectBox = $('<select></select>').appendTo(container); | ||||
|                         if (ui.opts && Array.isArray(ui.opts.opts)) { | ||||
|                             ui.opts.opts.forEach(function(o) { | ||||
|                                 var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale); | ||||
|                                 // $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox); | ||||
|                                 $('<option>').val(o.v).text(label).appendTo(selectBox); | ||||
|                             }) | ||||
|                         } | ||||
|                         selectBox.on('change', function(evt) { | ||||
|                             var v = selectBox.val(); | ||||
|                             // var parts = v.split(":"); | ||||
|                             // var t = parts.shift(); | ||||
|                             // v = parts.join(":"); | ||||
|                             // | ||||
|                             // valueField.typedInput("type",'str') | ||||
|                             valueField.typedInput("value",v) | ||||
|                         }); | ||||
|                         selectBox.val(valueField.typedInput("value")); | ||||
|                         // selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value")); | ||||
|                     }, | ||||
|                     expand: { | ||||
|                         icon: "fa-caret-down", | ||||
|                         minWidth: 400, | ||||
|                         content: function(container) { | ||||
|                             var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); | ||||
|                             var optList = $('<ol>').appendTo(content).editableList({ | ||||
|                                 header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"), | ||||
|                                 addItem: function(row,index,itemData) { | ||||
|                                     var labelDiv = $('<div>').appendTo(row); | ||||
|                                     var label = lookupLabel(itemData.l, "", currentLocale); | ||||
|                                     itemData.label = $('<input type="text">').val(label).appendTo(labelDiv); | ||||
|                                     itemData.label.on('keydown', function(evt) { | ||||
|                                         if (evt.keyCode === 13) { | ||||
|                                             itemData.input.focus(); | ||||
|                                             evt.preventDefault(); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv); | ||||
|                                     RED.popover.tooltip(labelIcon,function() { | ||||
|                                         return currentLocale; | ||||
|                                     }) | ||||
|                                     itemData.input = $('<input type="text">').val(itemData.v).appendTo(row); | ||||
|  | ||||
|                                     // Problem using a TI here: | ||||
|                                     //  - this is in a popout panel | ||||
|                                     //  - clicking the expand button in the TI will close the parent edit tray | ||||
|                                     //    and open the type editor. | ||||
|                                     //  - but it leaves the popout panel over the top. | ||||
|                                     //  - there is no way to get back to the popout panel after closing the type editor | ||||
|                                     //.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST}); | ||||
|                                     itemData.input.on('keydown', function(evt) { | ||||
|                                         if (evt.keyCode === 13) { | ||||
|                                             // Enter or Tab | ||||
|                                             var index = optList.editableList('indexOf',itemData); | ||||
|                                             var length = optList.editableList('length'); | ||||
|                                             if (index + 1 === length) { | ||||
|                                                 var newItem = {}; | ||||
|                                                 optList.editableList('addItem',newItem); | ||||
|                                                 setTimeout(function() { | ||||
|                                                     if (newItem.label) { | ||||
|                                                         newItem.label.focus(); | ||||
|                                                     } | ||||
|                                                 },100) | ||||
|                                             } else { | ||||
|                                                 var nextItem = optList.editableList('getItemAt',index+1); | ||||
|                                                 if (nextItem.label) { | ||||
|                                                     nextItem.label.focus() | ||||
|                                                 } | ||||
|                                             } | ||||
|                                             evt.preventDefault(); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                 }, | ||||
|                                 sortable: true, | ||||
|                                 removable: true, | ||||
|                                 height: 160 | ||||
|                             }) | ||||
|                             if (ui.opts.opts.length > 0) { | ||||
|                                 ui.opts.opts.forEach(function(o) { | ||||
|                                     optList.editableList('addItem',$.extend(true,{},o)) | ||||
|                                 }) | ||||
|                             } else { | ||||
|                                 optList.editableList('addItem',{}) | ||||
|                             } | ||||
|                             return { | ||||
|                                 onclose: function() { | ||||
|                                     var items = optList.editableList('items'); | ||||
|                                     var vals = []; | ||||
|                                     items.each(function (i,el) { | ||||
|                                         var data = el.data('data'); | ||||
|                                         var l = data.label.val().trim(); | ||||
|                                         var v = data.input.val(); | ||||
|                                         // var t = data.input.typedInput('type'); | ||||
|                                         // var v = data.input.typedInput('value'); | ||||
|                                         if (l.length > 0) { | ||||
|                                             data.l = data.l || {}; | ||||
|                                             data.l[currentLocale] = l; | ||||
|                                         } | ||||
|                                         data.v = v; | ||||
|  | ||||
|                                         if (l.length > 0 || v.length > 0) { | ||||
|                                             var val = {l:data.l,v:data.v}; | ||||
|                                             // if (t !== 'str') { | ||||
|                                             //     val.t = t; | ||||
|                                             // } | ||||
|                                             vals.push(val); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     ui.opts.opts = vals; | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"checkbox", | ||||
|                     label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         checkbox = $('<input type="checkbox">').appendTo(container); | ||||
|                         checkbox.on('change', function(evt) { | ||||
|                             valueField.typedInput('value',$(this).prop('checked')?"true":"false"); | ||||
|                         }) | ||||
|                         checkbox.prop('checked',valueField.typedInput('value')==="true"); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"spinner", | ||||
|                     label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false, | ||||
|                     valueLabel: function(container,value) { | ||||
|                         container.css("padding",0); | ||||
|                         var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container); | ||||
|  | ||||
|                         var input = $('<div class="placeholder-input">').appendTo(innerContainer); | ||||
|                         $('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input); | ||||
|  | ||||
|                         var min = ui.opts && ui.opts.min; | ||||
|                         var max = ui.opts && ui.opts.max; | ||||
|                         var label = ""; | ||||
|                         if (min !== undefined && max !== undefined) { | ||||
|                             label = Math.min(min,max)+" - "+Math.max(min,max); | ||||
|                         } else if (min !== undefined) { | ||||
|                             label = "> "+min; | ||||
|                         } else if (max !== undefined) { | ||||
|                             label = "< "+max; | ||||
|                         } | ||||
|                         $('<span>').css("margin-left","15px").text(label).appendTo(input); | ||||
|                     }, | ||||
|                     expand: { | ||||
|                         icon: "fa-caret-down", | ||||
|                         content: function(container) { | ||||
|                             var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); | ||||
|                             content.css("padding","8px 5px") | ||||
|                             var min = ui.opts.min; | ||||
|                             var max = ui.opts.max; | ||||
|                             var minInput = $('<input type="number" style="margin-bottom:0; width:60px">'); | ||||
|                             minInput.val(min); | ||||
|                             var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">'); | ||||
|                             maxInput.val(max); | ||||
|                             $('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content); | ||||
|                             $('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content); | ||||
|                             return { | ||||
|                                 onclose: function() { | ||||
|                                     var min = minInput.val().trim(); | ||||
|                                     var max = maxInput.val().trim(); | ||||
|                                     if (min !== "") { | ||||
|                                         ui.opts.min = parseInt(min); | ||||
|                                     } else { | ||||
|                                         delete ui.opts.min; | ||||
|                                     } | ||||
|                                     if (max !== "") { | ||||
|                                         ui.opts.max = parseInt(max); | ||||
|                                     } else { | ||||
|                                         delete ui.opts.max; | ||||
|                                     } | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"none", | ||||
|                     label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false | ||||
|                 }, | ||||
|                 { | ||||
|                     value:"hide", | ||||
|                     label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false | ||||
|                 } | ||||
|             ], | ||||
|             default: 'none' | ||||
|         }).on("typedinputtypechange", function(evt,type) { | ||||
|             ui.type = $(this).typedInput("type"); | ||||
|             ui.opts = typeOptions[ui.type]; | ||||
|             if (ui.type === 'input') { | ||||
|                 // In the case of 'input' type, the typedInput uses the multiple-option | ||||
|                 // mode. Its value needs to be set to a comma-separately list of the | ||||
|                 // selected options. | ||||
|                 inputCellInput.typedInput('value',ui.opts.types.join(",")) | ||||
|             } else { | ||||
|                 // No other type cares about `value`, but doing this will | ||||
|                 // force a refresh of the label now that `ui.opts` has | ||||
|                 // been updated. | ||||
|                 inputCellInput.typedInput('value',Date.now()) | ||||
|             } | ||||
|  | ||||
|             switch (ui.type) { | ||||
|                 case 'input': | ||||
|                     valueField.typedInput('types',ui.opts.types); | ||||
|                     break; | ||||
|                 case 'select': | ||||
|                     valueField.typedInput('types',['str']); | ||||
|                     break; | ||||
|                 case 'checkbox': | ||||
|                     valueField.typedInput('types',['bool']); | ||||
|                     break; | ||||
|                 case 'spinner': | ||||
|                     valueField.typedInput('types',['num']); | ||||
|                     break; | ||||
|                 case 'cred': | ||||
|                     valueField.typedInput('types',['cred']); | ||||
|                     break; | ||||
|                 default: | ||||
|                     valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST) | ||||
|             } | ||||
|             if (ui.type === 'checkbox') { | ||||
|                 valueField.typedInput('type','bool'); | ||||
|             } else if (ui.type === 'spinner') { | ||||
|                 valueField.typedInput('type','num'); | ||||
|             } | ||||
|             if (ui.type !== 'checkbox') { | ||||
|                 checkbox = null; | ||||
|             } | ||||
|  | ||||
|         }).on("change", function(evt,type) { | ||||
|             if (ui.type === 'input') { | ||||
|                 var types = inputCellInput.typedInput('value'); | ||||
|                 ui.opts.types = (types === "") ? ["str"] : types.split(","); | ||||
|                 valueField.typedInput('types',ui.opts.types); | ||||
|             } | ||||
|         }); | ||||
|         valueField.on("change", function(evt) { | ||||
|             if (checkbox) { | ||||
|                 checkbox.prop('checked',$(this).typedInput('value')==="true") | ||||
|             } | ||||
|         }) | ||||
|         // Set the input to the right type. This will trigger the 'typedinputtypechange' | ||||
|         // event handler (just above ^^) to update the value if needed | ||||
|         inputCellInput.typedInput('type',ui.type) | ||||
|     } | ||||
|  | ||||
|     function buildEnvUIRow(row, tenv, ui, node) { | ||||
|         ui.label = ui.label||{}; | ||||
| @@ -1540,7 +929,7 @@ RED.subflow = (function() { | ||||
|             ui.opts = {}; | ||||
|         } else if (!ui.type) { | ||||
|             ui.type = "input"; | ||||
|             ui.opts = {types:DEFAULT_ENV_TYPE_LIST} | ||||
|             ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST } | ||||
|         } else { | ||||
|             if (!ui.opts) { | ||||
|                 ui.opts = (ui.type === "select") ? {opts:[]} : {}; | ||||
| @@ -1549,7 +938,7 @@ RED.subflow = (function() { | ||||
|  | ||||
|         var labels = ui.label || {}; | ||||
|         var locale = RED.i18n.lang(); | ||||
|         var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, locale); | ||||
|         var labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"]||tenv.name, locale); | ||||
|         var label = $('<label>').appendTo(row); | ||||
|         $('<span> </span>').appendTo(row); | ||||
|         var labelContainer = $('<span></span>').appendTo(label); | ||||
| @@ -1602,7 +991,7 @@ RED.subflow = (function() { | ||||
|                 input = $('<select>').css('width','70%').appendTo(row); | ||||
|                 if (ui.opts.opts) { | ||||
|                     ui.opts.opts.forEach(function(o) { | ||||
|                         $('<option>').val(o.v).text(lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input); | ||||
|                         $('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input); | ||||
|                     }) | ||||
|                 } | ||||
|                 input.val(val.value); | ||||
| @@ -1668,9 +1057,8 @@ RED.subflow = (function() { | ||||
|      * @param uiContainer - container for UI | ||||
|      * @param envList - env var definitions of template | ||||
|      */ | ||||
|     function buildEnvUI(uiContainer, envList,node) { | ||||
|     function buildEnvUI(uiContainer, envList, node) { | ||||
|         uiContainer.empty(); | ||||
|         var elementID = 0; | ||||
|         for (var i = 0; i < envList.length; i++) { | ||||
|             var tenv = envList[i]; | ||||
|             if (tenv.ui && tenv.ui.type === 'hide') { | ||||
| @@ -1678,8 +1066,6 @@ RED.subflow = (function() { | ||||
|             } | ||||
|             var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer); | ||||
|             buildEnvUIRow(row,tenv, tenv.ui || {}, node); | ||||
|  | ||||
|             // console.log(ui); | ||||
|         } | ||||
|     } | ||||
|     // buildEnvUI | ||||
| @@ -1715,7 +1101,7 @@ RED.subflow = (function() { | ||||
|                             //     icon: "", | ||||
|                             //     label: {}, | ||||
|                             //     type: "input", | ||||
|                             //     opts: {types:DEFAULT_ENV_TYPE_LIST} | ||||
|                             //     opts: {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST} | ||||
|                             // } | ||||
|                             if (!ui.icon) { | ||||
|                                 delete ui.icon; | ||||
| @@ -1725,7 +1111,7 @@ RED.subflow = (function() { | ||||
|                             } | ||||
|                             switch (ui.type) { | ||||
|                                 case "input": | ||||
|                                     if (JSON.stringify(ui.opts) === JSON.stringify({types:DEFAULT_ENV_TYPE_LIST})) { | ||||
|                                     if (JSON.stringify(ui.opts) === JSON.stringify({types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST})) { | ||||
|                                         // This is the default input config. Delete it as it will | ||||
|                                         // be applied automatically | ||||
|                                         delete ui.type; | ||||
| @@ -1841,7 +1227,6 @@ RED.subflow = (function() { | ||||
|  | ||||
|     function exportSubflowInstanceEnv(node) { | ||||
|         var env = []; | ||||
|  | ||||
|         // First, get the values for the SubflowTemplate defined properties | ||||
|         //  - these are the ones with custom UI elements | ||||
|         var parentEnv = getSubflowInstanceParentEnv(node); | ||||
| @@ -1853,7 +1238,7 @@ RED.subflow = (function() { | ||||
|                     ui.type = "cred"; | ||||
|                 } else { | ||||
|                     ui.type = "input"; | ||||
|                     ui.opts = {types:DEFAULT_ENV_TYPE_LIST} | ||||
|                     ui.opts = {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST} | ||||
|                 } | ||||
|             } else { | ||||
|                 ui.opts = ui.opts || {}; | ||||
| @@ -1893,22 +1278,6 @@ RED.subflow = (function() { | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         // Second, get the values from the Properties table tab | ||||
|         var items = $('#red-ui-editor-subflow-env-list').editableList('items'); | ||||
|         items.each(function (i,el) { | ||||
|             var data = el.data('data'); | ||||
|             var item; | ||||
|             if (data.nameField && data.valueField) { | ||||
|                 item = { | ||||
|                     name: data.nameField.val(), | ||||
|                     value: data.valueField.typedInput("value"), | ||||
|                     type: data.valueField.typedInput("type") | ||||
|                 } | ||||
|                 if (item.name.trim() !== "") { | ||||
|                     env.push(item); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         return env; | ||||
|     } | ||||
|  | ||||
| @@ -1916,164 +1285,18 @@ RED.subflow = (function() { | ||||
|         return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Lookup text for specific locale | ||||
|      * @param labels - dict of labels | ||||
|      * @param defaultLabel - fallback label if not found | ||||
|      * @param locale - target locale | ||||
|      * @returns {string} text for specified locale | ||||
|      */ | ||||
|     function lookupLabel(labels, defaultLabel, locale) { | ||||
|         if (labels) { | ||||
|             if (labels[locale]) { | ||||
|                 return labels[locale]; | ||||
|             } | ||||
|             if (locale) { | ||||
|                 var lang = locale.substring(0, 2); | ||||
|                 if (labels[lang]) { | ||||
|                     return labels[lang]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return defaultLabel; | ||||
|     } | ||||
|  | ||||
|     // Called by subflow.oneditprepare for both instances and templates | ||||
|     function buildEditForm(type,node) { | ||||
|         if (type === "subflow-template") { | ||||
|             buildPropertiesList($('#node-input-env-container'), node); | ||||
|             // This is the tabbed UI that offers the env list - with UI options | ||||
|             // plus the preview tab | ||||
|             buildEnvControl($('#node-input-env-container'), node); | ||||
|             RED.editor.envVarList.create($('#node-input-env-container'), node); | ||||
|         } else  if (type === "subflow") { | ||||
|             // This gets called by the subflow type `oneditprepare` function | ||||
|             // registered in nodes.js#addSubflow() | ||||
|             // This is the rendered version of the subflow env var list | ||||
|             buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node), node); | ||||
|         } | ||||
|     } | ||||
|     function buildPropertiesForm(node) { | ||||
|         var container = $('#editor-subflow-envProperties-content'); | ||||
|         var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container); | ||||
|         var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form); | ||||
|         var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer); | ||||
|         buildPropertiesList(list, node); | ||||
|     } | ||||
|  | ||||
|     function setupInputValidation(input,validator) { | ||||
|         var errorTip; | ||||
|         var validateTimeout; | ||||
|  | ||||
|         var validateFunction = function() { | ||||
|             if (validateTimeout) { | ||||
|                 return; | ||||
|             } | ||||
|             validateTimeout = setTimeout(function() { | ||||
|                 var error = validator(input.val()); | ||||
|                 // if (!error && errorTip) { | ||||
|                 //     errorTip.close(); | ||||
|                 //     errorTip = null; | ||||
|                 // } else if (error && !errorTip) { | ||||
|                 //     errorTip = RED.popover.create({ | ||||
|                 //         tooltip: true, | ||||
|                 //         target:input, | ||||
|                 //         size: "small", | ||||
|                 //         direction: "bottom", | ||||
|                 //         content: error, | ||||
|                 //     }).open(); | ||||
|                 // } | ||||
|                 input.toggleClass("input-error",!!error); | ||||
|                 validateTimeout = null; | ||||
|             }) | ||||
|         } | ||||
|         input.on("change keyup paste", validateFunction); | ||||
|     } | ||||
|  | ||||
|     function buildModuleForm(container, node) { | ||||
|         $(_subflowModulePaneTemplate).appendTo(container); | ||||
|         var moduleProps = node.meta || {}; | ||||
|         [ | ||||
|             'module', | ||||
|             'type', | ||||
|             'version', | ||||
|             'author', | ||||
|             'desc', | ||||
|             'keywords', | ||||
|             'license' | ||||
|         ].forEach(function(property) { | ||||
|             $("#subflow-input-module-"+property).val(moduleProps[property]||"") | ||||
|         }) | ||||
|         $("#subflow-input-module-type").attr("placeholder",node.id); | ||||
|  | ||||
|         setupInputValidation($("#subflow-input-module-module"), function(newValue) { | ||||
|             newValue = newValue.trim(); | ||||
|             var isValid = newValue.length < 215; | ||||
|             isValid = isValid && !/^[._]/.test(newValue); | ||||
|             isValid = isValid && !/[A-Z]/.test(newValue); | ||||
|             if (newValue !== encodeURIComponent(newValue)) { | ||||
|                 var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue); | ||||
|                 if (m) { | ||||
|                     isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2])) | ||||
|                 } else { | ||||
|                     isValid = false; | ||||
|                 } | ||||
|             } | ||||
|             return isValid?"":"Invalid module name" | ||||
|         }) | ||||
|         setupInputValidation($("#subflow-input-module-version"), function(newValue) { | ||||
|             newValue = newValue.trim(); | ||||
|             var isValid = newValue === "" || | ||||
|                           /^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue); | ||||
|             return isValid?"":"Invalid version number" | ||||
|         }) | ||||
|  | ||||
|         var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"]; | ||||
|         var typedLicenses = { | ||||
|             types: licenses.map(function(l) { | ||||
|                 return { | ||||
|                     value: l, | ||||
|                     label: l === "none" ? RED._("editor:subflow.licenseNone") : l, | ||||
|                     hasValue: false | ||||
|                 }; | ||||
|             }) | ||||
|         } | ||||
|         typedLicenses.types.push({ | ||||
|             value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg" | ||||
|         }) | ||||
|         if (!moduleProps.license) { | ||||
|             typedLicenses.default = "none"; | ||||
|         } else if (licenses.indexOf(moduleProps.license) > -1) { | ||||
|             typedLicenses.default = moduleProps.license; | ||||
|         } else { | ||||
|             typedLicenses.default = "_custom_"; | ||||
|         } | ||||
|         $("#subflow-input-module-license").typedInput(typedLicenses) | ||||
|     } | ||||
|  | ||||
|     function exportSubflowModuleProperties(node) { | ||||
|         var value; | ||||
|         var moduleProps = {}; | ||||
|         [ | ||||
|             'module', | ||||
|             'type', | ||||
|             'version', | ||||
|             'author', | ||||
|             'desc', | ||||
|             'keywords' | ||||
|         ].forEach(function(property) { | ||||
|             value = $("#subflow-input-module-"+property).val().trim(); | ||||
|             if (value) { | ||||
|                 moduleProps[property] = value; | ||||
|             } | ||||
|         }) | ||||
|         var selectedLicenseType = $("#subflow-input-module-license").typedInput("type"); | ||||
|  | ||||
|         if (selectedLicenseType === '_custom_') { | ||||
|             value = $("#subflow-input-module-license").val(); | ||||
|             if (value) { | ||||
|                 moduleProps.license = value; | ||||
|             } | ||||
|         } else if (selectedLicenseType !== "none") { | ||||
|             moduleProps.license = selectedLicenseType; | ||||
|         } | ||||
|         return moduleProps; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
| @@ -2085,14 +1308,9 @@ RED.subflow = (function() { | ||||
|         removeOutput: removeSubflowOutput, | ||||
|         removeStatus: removeSubflowStatus, | ||||
|  | ||||
|  | ||||
|         buildEditForm: buildEditForm, | ||||
|         buildPropertiesForm: buildPropertiesForm, | ||||
|         buildModuleForm: buildModuleForm, | ||||
|  | ||||
|         exportSubflowTemplateEnv: exportEnvList, | ||||
|         exportSubflowInstanceEnv: exportSubflowInstanceEnv, | ||||
|         exportSubflowModuleProperties: exportSubflowModuleProperties | ||||
|  | ||||
|         exportSubflowInstanceEnv: exportSubflowInstanceEnv | ||||
|     } | ||||
| })(); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user