Compare commits
664 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c6ad2c9ad2 | ||
|
3b44d9972e | ||
|
af736c98f2 | ||
|
f1377fa217 | ||
|
2ba146b9ff | ||
|
2361607aa3 | ||
|
86ffc80098 | ||
|
7f6915eb59 | ||
|
d69bcad028 | ||
|
4cb45e2712 | ||
|
b7a0ad703a | ||
|
7610b9a975 | ||
|
7d95f621df | ||
|
bba210e112 | ||
|
3a97e20bde | ||
|
4fe7ea00b0 | ||
|
3ec8ecd4de | ||
|
401e65e852 | ||
|
e7c5b691a0 | ||
|
9f3ea8da67 | ||
|
4d84d624b1 | ||
|
633a6a0ee6 | ||
|
c7bcd3f438 | ||
|
d3a29a6f16 | ||
|
827711ca89 | ||
|
76e98f74fa | ||
|
fb09f4b22d | ||
|
bb06585748 | ||
|
c76ba1dcc7 | ||
|
a115301b04 | ||
|
72917117a9 | ||
|
6567739236 | ||
|
4aa6b47c0e | ||
|
03558b012c | ||
|
3288efdad6 | ||
|
3902a343f3 | ||
|
882b7d0391 | ||
|
81f082825d | ||
|
392fd6fed3 | ||
|
51afed4041 | ||
|
17e3b71d9c | ||
|
6e75089f3a | ||
|
6dc640b129 | ||
|
27cbaac343 | ||
|
fa4006619e | ||
|
cb8fe8462a | ||
|
abd51a5511 | ||
|
a0cc1e6b0c | ||
|
50399c6bfa | ||
|
de48c1be44 | ||
|
0786ec4b66 | ||
|
db319e0ebc | ||
|
4fc568856a | ||
|
4c6771669b | ||
|
9bca2a91c9 | ||
|
66eaaf5a48 | ||
|
9837f0e2e1 | ||
|
6b8ffb4c68 | ||
|
f35dd34da9 | ||
|
ed19e4fa08 | ||
|
661e1a4f90 | ||
|
5826de76ca | ||
|
05888740e5 | ||
|
41f3b0c333 | ||
|
70f3e72a20 | ||
|
e873afd40b | ||
|
2777c2937a | ||
|
798903e4cc | ||
|
58622ba18f | ||
|
c368dcd5b7 | ||
|
0b4c652ce7 | ||
|
dbaacc411a | ||
|
1850185d1e | ||
|
2e9d445d36 | ||
|
aed89d82fb | ||
|
231adac6d8 | ||
|
587c4e5915 | ||
|
55f1cbf18f | ||
|
38168a545b | ||
|
43c6df49d7 | ||
|
f1c59faf72 | ||
|
5f7019325c | ||
|
fe4dae8518 | ||
|
1f848b205b | ||
|
742c470d81 | ||
|
5ead3342cc | ||
|
b95dc2ecce | ||
|
4d0950215f | ||
|
da0ce9fe0d | ||
|
ca62e720b5 | ||
|
c4b1795396 | ||
|
fd2e47ed73 | ||
|
d5f2255a68 | ||
|
05b58e9263 | ||
|
4a91c27e4b | ||
|
3a03d46d8d | ||
|
f03aff7006 | ||
|
043b8a3105 | ||
|
1dd9984521 | ||
|
d2be7f8c8f | ||
|
88dc202db2 | ||
|
083d54b008 | ||
|
87d77efa57 | ||
|
35c4a41d7b | ||
|
1ca3ca07d5 | ||
|
d673846e3d | ||
|
f62b7afede | ||
|
e65770a53a | ||
|
a92a741932 | ||
|
45f67191ba | ||
|
93f5da325b | ||
|
8fb955e182 | ||
|
9f5e6a4b37 | ||
|
f43738446e | ||
|
923a46d304 | ||
|
b9b5eaccae | ||
|
cda11491c2 | ||
|
98c539f662 | ||
|
9fb958b302 | ||
|
8e25e76439 | ||
|
62694da7e6 | ||
|
86064651af | ||
|
65daaeb617 | ||
|
08b39f50b3 | ||
|
d0f7e5ca4d | ||
|
4eb5058e68 | ||
|
1054193298 | ||
|
38c6cf0450 | ||
|
5b04b86867 | ||
|
a074bcfd56 | ||
|
f93179d946 | ||
|
2c347bc092 | ||
|
0f7119f468 | ||
|
2685a24705 | ||
|
371f72f4f1 | ||
|
c70c00043b | ||
|
50d0a88276 | ||
|
5bbf576dae | ||
|
5d334e9619 | ||
|
98f9353338 | ||
|
d3de7037e5 | ||
|
64431c6711 | ||
|
d4ce193dc8 | ||
|
606305aec4 | ||
|
a95f44d68b | ||
|
ef2dc4b9e1 | ||
|
9baca1772b | ||
|
04cd19349d | ||
|
1280e5bc8b | ||
|
dda90f956d | ||
|
bc4b599513 | ||
|
090d52d678 | ||
|
a47ad4842a | ||
|
3d5ed840dc | ||
|
11d75ff581 | ||
|
306fb7a3d1 | ||
|
0839b6f58e | ||
|
fceca703b3 | ||
|
4dc60d2477 | ||
|
d840d0b67d | ||
|
43dad4c465 | ||
|
60812b2d8a | ||
|
35e2caff13 | ||
|
1d9d5c6bc7 | ||
|
4d99536ea7 | ||
|
34537180c3 | ||
|
cb01920ee6 | ||
|
437b01a0ff | ||
|
075a2abf71 | ||
|
985875cc75 | ||
|
1c45bc615f | ||
|
fa7f2606fb | ||
|
12b95f1c72 | ||
|
a0aee2021d | ||
|
c90fd1e6d8 | ||
|
3b769fd2de | ||
|
71ecb89abc | ||
|
7b6bc1d3bc | ||
|
9c3be40fbe | ||
|
ab87fa9ce4 | ||
|
d1940a023a | ||
|
5a176a037c | ||
|
ec25191c98 | ||
|
20b321f928 | ||
|
425b016d63 | ||
|
b2c7189ce4 | ||
|
f66886dbdb | ||
|
712f8b4680 | ||
|
f626ee060a | ||
|
86aa7c97be | ||
|
30e3525987 | ||
|
ad44f838da | ||
|
2569a35b6c | ||
|
1ee5e50d50 | ||
|
1dbec5eca8 | ||
|
2bc8db308c | ||
|
f196740426 | ||
|
20121b79c5 | ||
|
741a4cfe53 | ||
|
0343de9f34 | ||
|
4772bca14a | ||
|
6ae1a5ba0d | ||
|
217c9718e4 | ||
|
61d7893467 | ||
|
8f26c01f4b | ||
|
61045ddd7f | ||
|
1bf72a0bc3 | ||
|
6d84b1bb8d | ||
|
8abd0b1fdf | ||
|
81e125b7ba | ||
|
d5e1468718 | ||
|
c232bf5ed6 | ||
|
21b25ffaee | ||
|
ca0a93df08 | ||
|
699a22c757 | ||
|
8b2b1669b5 | ||
|
c1e8370916 | ||
|
ddedea8b90 | ||
|
8f414ce458 | ||
|
453f23da20 | ||
|
9e91e42a1b | ||
|
b666734c79 | ||
|
a2297f303d | ||
|
ecde942255 | ||
|
d668d43a0a | ||
|
ca91a5dd95 | ||
|
5f9780d71c | ||
|
ee37464741 | ||
|
8d73f927db | ||
|
4a0222bd1c | ||
|
c0b8f5e3e1 | ||
|
a9a0b263dc | ||
|
f2b73187d8 | ||
|
ef10ade0cc | ||
|
719bb4263e | ||
|
b3602b268e | ||
|
66ec9bae27 | ||
|
d96b6e77c0 | ||
|
da64c018ac | ||
|
f9fb97adf2 | ||
|
8316bc6480 | ||
|
08021e039a | ||
|
cc6e0937a0 | ||
|
c1d694a97c | ||
|
fcf4f40c36 | ||
|
380b03399c | ||
|
c33d02c53f | ||
|
fa5e37993e | ||
|
437b2d506b | ||
|
4ed09f6431 | ||
|
0b98a6acf8 | ||
|
1d73c86cb2 | ||
|
40fe0f3239 | ||
|
d1ea689999 | ||
|
a6644ad5ff | ||
|
658746d2a3 | ||
|
cbdd4de630 | ||
|
00c612485b | ||
|
3a6192bf73 | ||
|
c64b5c2850 | ||
|
fdbf079896 | ||
|
d1a5395727 | ||
|
83a3642c0e | ||
|
9932d34304 | ||
|
7aa37a1976 | ||
|
fa42fbdab8 | ||
|
caa83ac830 | ||
|
3963fa9738 | ||
|
005a98d020 | ||
|
9560dc9408 | ||
|
4ac9a5edf0 | ||
|
37e62597ae | ||
|
90bfe378d0 | ||
|
ce22b494ec | ||
|
f9e0420647 | ||
|
2fe568d9ba | ||
|
2d4979df4d | ||
|
b555b014b8 | ||
|
999b888c54 | ||
|
5193d7bddb | ||
|
6b03379e4e | ||
|
08d687ad60 | ||
|
eb57089f06 | ||
|
a76e4fede1 | ||
|
705d043540 | ||
|
5462e251f8 | ||
|
50788af6ca | ||
|
1a07c5a329 | ||
|
9fb81b2814 | ||
|
10ad7fbf6e | ||
|
811c4f2630 | ||
|
f7e3b0a64f | ||
|
7d83d76fb3 | ||
|
d3c41b38f7 | ||
|
57d6b16d5c | ||
|
27aa5ae7db | ||
|
62e8f564b9 | ||
|
a1d7bb4208 | ||
|
5d8dae05c4 | ||
|
6bde07b5a0 | ||
|
846ab08661 | ||
|
999cf66b27 | ||
|
60539d890b | ||
|
e5a0f25d94 | ||
|
fde9d40098 | ||
|
f70e9ea076 | ||
|
d0af4aac4d | ||
|
7de3704210 | ||
|
1c33b837b8 | ||
|
95d20d7fba | ||
|
90b8806e7c | ||
|
39df80bf99 | ||
|
bac4beae03 | ||
|
a7b68c18b5 | ||
|
ef2360baee | ||
|
8716e7e601 | ||
|
00c2dae969 | ||
|
8782bc5896 | ||
|
6359b90352 | ||
|
6cfa4976fe | ||
|
35cd7cf2b8 | ||
|
b2d7f079b7 | ||
|
726069bc4b | ||
|
fbccf01933 | ||
|
c9f3c6f4a3 | ||
|
c47da013ff | ||
|
bc76499957 | ||
|
24afcff0ea | ||
|
6777f24845 | ||
|
61e0923fc4 | ||
|
757caeb9a4 | ||
|
fe9dc6b272 | ||
|
24efdbe4da | ||
|
9ca102cf81 | ||
|
48df31d7b7 | ||
|
539afb1e1d | ||
|
bdcba44ca5 | ||
|
99a51b07ac | ||
|
5fbaca75b4 | ||
|
a6974371b0 | ||
|
409fa49234 | ||
|
9bbd6a70b8 | ||
|
f0b4cb608a | ||
|
284d7e26d1 | ||
|
965c0937ac | ||
|
d9cf6a4431 | ||
|
e6ed8ee509 | ||
|
2563649b3e | ||
|
203bc41b06 | ||
|
cb1d18c7c8 | ||
|
5ea68dafc4 | ||
|
68bb8252af | ||
|
406f742d29 | ||
|
5522e57f65 | ||
|
94e27dbfc5 | ||
|
f5fc8f763f | ||
|
9058bf615c | ||
|
9d17137dec | ||
|
4a318553f7 | ||
|
0017074d38 | ||
|
a39a26fcc2 | ||
|
2fe859b111 | ||
|
c105b2df37 | ||
|
4fb86ab55a | ||
|
1ed98a5963 | ||
|
a4a29ceb3c | ||
|
6249083431 | ||
|
aa18c65fa8 | ||
|
a7900940da | ||
|
008bc98070 | ||
|
0705589cc2 | ||
|
b2caba593f | ||
|
6d4c64fcd5 | ||
|
7d41781fb4 | ||
|
0760facb77 | ||
|
a0ce095807 | ||
|
df0110913a | ||
|
06731374a4 | ||
|
6c8b7c0082 | ||
|
93136961b9 | ||
|
529a691e1d | ||
|
9f0b3eba47 | ||
|
6d897793cb | ||
|
0e12fc6b02 | ||
|
c00558ea1b | ||
|
bacf27a3ca | ||
|
6560ea0630 | ||
|
8338231ce5 | ||
|
5813a91244 | ||
|
dfd8ab3545 | ||
|
a5b9b949a8 | ||
|
72570e4510 | ||
|
3002aead6b | ||
|
841edbe6fb | ||
|
2b9aca0c56 | ||
|
3a17c3ee6d | ||
|
df09252ee0 | ||
|
f51778d417 | ||
|
119fc63794 | ||
|
ee3425d3be | ||
|
31c979f30f | ||
|
89f2c26cd6 | ||
|
0dbf43d0aa | ||
|
8a6d11b191 | ||
|
d8eb926e2c | ||
|
21d0adbdae | ||
|
c5fd3a5753 | ||
|
eae4e3d983 | ||
|
bb1fe8daef | ||
|
c5d8e09b41 | ||
|
369eae3d92 | ||
|
e2fa457ca2 | ||
|
871f764e98 | ||
|
f8853af902 | ||
|
4248d20f39 | ||
|
5cda08e7b0 | ||
|
7e9d96ee87 | ||
|
974ac31d33 | ||
|
0658b70631 | ||
|
53258eeede | ||
|
4f174308b9 | ||
|
98c9e40349 | ||
|
3a4756bd83 | ||
|
6ccb05cb2c | ||
|
3c2d32b867 | ||
|
956050434f | ||
|
38ab1550d2 | ||
|
e852d1e57c | ||
|
7de0216976 | ||
|
911288e695 | ||
|
72e1f20383 | ||
|
d28a6eaf9d | ||
|
17f3366556 | ||
|
0bef04ae0a | ||
|
f11b906fd9 | ||
|
518358d9dc | ||
|
5ffde21d83 | ||
|
052302b3e7 | ||
|
fe1ce21114 | ||
|
ce5c9da107 | ||
|
cf25b2866e | ||
|
07fd5a5f5f | ||
|
913fdac671 | ||
|
7dc838dea6 | ||
|
7112fd2a22 | ||
|
56e8c143dd | ||
|
7b4cbbe816 | ||
|
8f8ee4662d | ||
|
86013c7db4 | ||
|
01aa3324f8 | ||
|
b9cfeee965 | ||
|
b3684a70b5 | ||
|
51fce9343b | ||
|
0c5c3448d0 | ||
|
deaef3ab86 | ||
|
a443491c0c | ||
|
276d893198 | ||
|
653d0e71e4 | ||
|
faa7d948a7 | ||
|
771342989e | ||
|
e9ce519e4b | ||
|
c016b102eb | ||
|
0583c60837 | ||
|
1c1a85dcef | ||
|
c71e76335b | ||
|
c1a32c4eb9 | ||
|
9ad1f769d3 | ||
|
87f8fd34b8 | ||
|
e206d2919e | ||
|
9d809aa2ba | ||
|
8f744794e4 | ||
|
78ab4217be | ||
|
d090df94c5 | ||
|
937f26da41 | ||
|
98e3ff014e | ||
|
6f84526364 | ||
|
105d38c885 | ||
|
d7bdcd69fc | ||
|
87e537da90 | ||
|
8f16695f06 | ||
|
8403f6291f | ||
|
5af6ac3e80 | ||
|
0d557094b2 | ||
|
a2aa78afd4 | ||
|
b0de8abb63 | ||
|
6ff540ed08 | ||
|
2b8ed9850b | ||
|
dcd579b5e3 | ||
|
c9d2d301aa | ||
|
1aaef598a5 | ||
|
73d1f3d0e8 | ||
|
e369ded6c5 | ||
|
269846c587 | ||
|
8dc98420db | ||
|
1014abe92f | ||
|
6927f10f8f | ||
|
49d3a7190a | ||
|
a2e65b0018 | ||
|
f48ee01a03 | ||
|
0e926c566b | ||
|
d2c4c2c34c | ||
|
c2253d1e25 | ||
|
eae16b6e8c | ||
|
868ae5b5dd | ||
|
1406503e10 | ||
|
9ca9d88546 | ||
|
203d3f672c | ||
|
698b2688f6 | ||
|
be1620dd07 | ||
|
e1f0969957 | ||
|
e1dd8cf2ab | ||
|
8ee90777ee | ||
|
2fe9c1e55f | ||
|
9dd7e2e43d | ||
|
5efbdf5d04 | ||
|
5be3472413 | ||
|
a9a0953653 | ||
|
d4ac4c44d0 | ||
|
f459ff8ad0 | ||
|
b96ea36b70 | ||
|
e543cc0fed | ||
|
0b1b4df210 | ||
|
e59ffb0b19 | ||
|
120c8f2c28 | ||
|
fbfc5c8a2d | ||
|
31b018c80e | ||
|
255d708fb6 | ||
|
78d1da5fbc | ||
|
9c22a770ef | ||
|
b201828236 | ||
|
2a8a885271 | ||
|
7adefd6ee0 | ||
|
f967a5ecdc | ||
|
c8d6dc2531 | ||
|
216b5fba7a | ||
|
f4ec9a72d5 | ||
|
cf0c2825eb | ||
|
be46c419dc | ||
|
62c68d06fe | ||
|
4f4d8419bc | ||
|
16e17954b4 | ||
|
cc1d080a5a | ||
|
dd7f4f6752 | ||
|
9daeba02b5 | ||
|
8a96dbd121 | ||
|
2a57d0b6d0 | ||
|
8a5c1bade5 | ||
|
fcc6943f98 | ||
|
72a9de058d | ||
|
8748be28b7 | ||
|
20bdea7ae0 | ||
|
e19b8d35a9 | ||
|
81df74dfc8 | ||
|
153fa7478f | ||
|
500e9a4010 | ||
|
5352fc87ee | ||
|
f07fd64ffb | ||
|
36f299c031 | ||
|
78cf310c58 | ||
|
18a3d71024 | ||
|
26db1048f9 | ||
|
b61a250d58 | ||
|
1d10eba0cc | ||
|
35042132fa | ||
|
57c049b49f | ||
|
7a0ce0c957 | ||
|
eb4cadb0b5 | ||
|
ac0ca083c0 | ||
|
9afb4a9315 | ||
|
df065e94b7 | ||
|
a9789697e7 | ||
|
2d91be8814 | ||
|
5c58b0c2f4 | ||
|
f0139f9808 | ||
|
5610a3184e | ||
|
b202c73708 | ||
|
dd4cec84bf | ||
|
a1dac1e290 | ||
|
e199d6725e | ||
|
aef38b945d | ||
|
cd5eac2cbb | ||
|
8fea443e71 | ||
|
2a47951e46 | ||
|
5234fda266 | ||
|
e63067cd9f | ||
|
be61cf6a88 | ||
|
5efc89d514 | ||
|
9c104faff3 | ||
|
cf8fe16b09 | ||
|
71db193675 | ||
|
9952d9451e | ||
|
fb738ad9fa | ||
|
46f2f752b0 | ||
|
42730b8fce | ||
|
1c2be579d9 | ||
|
51e891ff88 | ||
|
731efe1c01 | ||
|
f77dd06e65 | ||
|
af20f3df64 | ||
|
4078212089 | ||
|
7bdb3181e2 | ||
|
933608aec1 | ||
|
1d7f06bbba | ||
|
f26cadab7f | ||
|
eacf41a4f6 | ||
|
ab3e64271b | ||
|
e26ea14104 | ||
|
3967e23828 | ||
|
1f8c6f87c9 | ||
|
f6203fe60a | ||
|
06bf710515 | ||
|
0f3cc3196c | ||
|
4403a00651 | ||
|
9c46feb22b | ||
|
10277aa956 | ||
|
ff093d98c6 | ||
|
acc0e0875b | ||
|
69f85bd688 | ||
|
910d983b82 | ||
|
128415bc9e | ||
|
082ce798d8 | ||
|
234abd82a2 | ||
|
3cbc1bbb1b | ||
|
0ed9f6cc4f | ||
|
10b092a9a7 | ||
|
444a897410 | ||
|
e013afb053 | ||
|
34364f5627 | ||
|
cef378d820 | ||
|
a27353c166 | ||
|
bbd197c71d | ||
|
fabf013714 | ||
|
81dcfecb4e | ||
|
971a62ebc9 | ||
|
04f2c92ba6 | ||
|
00d0f8cfc7 | ||
|
c5c404ea05 | ||
|
c80a44933c | ||
|
5599b999ec | ||
|
172cbdaa84 | ||
|
a3c4f12764 | ||
|
bf1cd457cd | ||
|
8af50a51ba | ||
|
ddf31e87b2 | ||
|
5adbc012f3 | ||
|
393fc349b9 | ||
|
dfed4963ed | ||
|
131adb6f4e | ||
|
a8b3cbb683 | ||
|
e97d5c7354 | ||
|
061c44f958 | ||
|
f5d8433341 | ||
|
f78a71e8ed | ||
|
4d48c72146 | ||
|
71ff828947 | ||
|
b6245bdef7 | ||
|
ce1cd1ab9c | ||
|
54b0debb3b | ||
|
1876b56022 | ||
|
d148a23ed6 | ||
|
049a5f1be6 | ||
|
f3880b7601 | ||
|
fbb45a8961 | ||
|
b8c460b825 |
24
.gitignore
vendored
@@ -1,11 +1,19 @@
|
||||
node_modules
|
||||
credentials.json
|
||||
flows*.json
|
||||
.DS_store
|
||||
.config.json
|
||||
.dist
|
||||
.jshintignore
|
||||
.npm
|
||||
.project
|
||||
.sessions.json
|
||||
.settings
|
||||
.tern-project
|
||||
*.backup
|
||||
*_cred*
|
||||
coverage
|
||||
credentials.json
|
||||
flows*.json
|
||||
nodes/node-red-nodes/
|
||||
.npm
|
||||
/coverage
|
||||
.config.json
|
||||
.sessions.json
|
||||
|
||||
node_modules
|
||||
public
|
||||
locales/zz-ZZ
|
||||
nodes/core/locales/zz-ZZ
|
||||
|
13
.jshintrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"asi": true, // allow missing semicolons
|
||||
"curly": true, // require braces
|
||||
"eqnull": true, // ignore ==null
|
||||
"forin": true, // require property filtering in "for in" loops
|
||||
"immed": true, // require immediate functions to be wrapped in ( )
|
||||
"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
//"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
//"unused": true, // Check for unused functions and variables
|
||||
"loopfunc": true, // allow functions to be defined in loops
|
||||
//"expr": true, // allow ternery operator syntax...
|
||||
"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
.git/*
|
||||
*.json
|
||||
lib/*
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
||||
|
7
.npmignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.settings
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.project
|
||||
.tern-project
|
||||
.travis.yml
|
||||
.git
|
19
.travis.yml
@@ -1,9 +1,24 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
env:
|
||||
- CXX="g++-4.8"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- gcc-4.8
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "5"
|
||||
before_install:
|
||||
- npm install -g npm@~1.4.18
|
||||
- npm install -g npm@latest-2
|
||||
node_js:
|
||||
- "5"
|
||||
- "4"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
script:
|
||||
- istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage
|
||||
before_script:
|
||||
|
@@ -6,11 +6,10 @@ We welcome contributions, but request you follow these guidelines.
|
||||
- [Feature requests](#feature-requests)
|
||||
- [Pull-Requests](#pull-requests)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
|
||||
|
||||
## Raising issues
|
||||
|
||||
Please raise any bug reports on the project's
|
||||
[issue tracker](https://github.com/node-red/node-red/issues?state=open). Be sure to
|
||||
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
||||
search the list to see if your issue has already been raised.
|
||||
|
||||
A good bug report is one that make it easy for us to understand what you were
|
||||
@@ -25,7 +24,6 @@ At a minimum, please include:
|
||||
- Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly.
|
||||
- Version of node.js - what does `node -v` say?
|
||||
|
||||
|
||||
## Feature requests
|
||||
|
||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
@@ -33,15 +31,15 @@ For feature requests, please raise them on the [mailing list](https://groups.goo
|
||||
## Pull-Requests
|
||||
|
||||
If you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
In order for us to accept pull-requests, the contributor must first complete
|
||||
a Contributor License Agreement (CLA). This clarifies the intellectual
|
||||
property license granted with any contribution. It is for your protection as a
|
||||
Contributor as well as the protection of IBM and its customers; it does not
|
||||
a Contributor License Agreement (CLA). This clarifies the intellectual
|
||||
property license granted with any contribution. It is for your protection as a
|
||||
Contributor as well as the protection of IBM and its customers; it does not
|
||||
change your rights to use your own Contributions for any other purpose.
|
||||
|
||||
You can download the CLAs here:
|
||||
@@ -59,13 +57,5 @@ code base. Some basic rules include:
|
||||
|
||||
- all files must have the Apache license in the header.
|
||||
- indent with 4-spaces, no tabs. No arguments.
|
||||
- opening brace on same line as `if`/`for`/`function`/etc, closing brace on its
|
||||
own line.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- opening brace on same line as `if`/`for`/`function` and so on, closing brace
|
||||
on its own line.
|
||||
|
470
Gruntfile.js
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2014 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,82 +14,410 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
|
||||
var nodemonArgs = ["-v"];
|
||||
var flowFile = grunt.option('flowFile');
|
||||
if (flowFile) {
|
||||
nodemonArgs.push(flowFile);
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
dist: ".dist"
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
// http://www.jshint.com/docs/options/
|
||||
"asi": true, // allow missing semicolons
|
||||
"curly": true, // require braces
|
||||
"eqnull": true, // ignore ==null
|
||||
"forin": true, // require property filtering in "for in" loops
|
||||
"immed": true, // require immediate functions to be wrapped in ( )
|
||||
"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
//"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
"loopfunc": true, // allow functions to be defined in loops
|
||||
"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/**/*.js',
|
||||
'public/red/**/*.js'
|
||||
],
|
||||
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/**/*.js' ]
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'public/red/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
files: {
|
||||
src: ['test/**/*.js']
|
||||
},
|
||||
options: {
|
||||
"expr": true
|
||||
}
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc:true
|
||||
// http://www.jshint.com/docs/options/
|
||||
//"asi": true, // allow missing semicolons
|
||||
//"curly": true, // require braces
|
||||
//"eqnull": true, // ignore ==null
|
||||
//"forin": true, // require property filtering in "for in" loops
|
||||
//"immed": true, // require immediate functions to be wrapped in ( )
|
||||
//"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
////"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
//"loopfunc": true, // allow functions to be defined in loops
|
||||
//"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/core/*/*.js',
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/core/*/*.js' ]
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'editor/js/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
files: {
|
||||
src: ['test/**/*.js']
|
||||
},
|
||||
options: {
|
||||
"expr": true
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
separator: ";",
|
||||
},
|
||||
build: {
|
||||
src: [
|
||||
// Ensure editor source files are concatenated in
|
||||
// the right order
|
||||
"editor/js/main.js",
|
||||
"editor/js/events.js",
|
||||
"editor/js/i18n.js",
|
||||
"editor/js/settings.js",
|
||||
"editor/js/user.js",
|
||||
"editor/js/comms.js",
|
||||
"editor/js/ui/state.js",
|
||||
"editor/js/nodes.js",
|
||||
"editor/js/history.js",
|
||||
"editor/js/validators.js",
|
||||
"editor/js/ui/deploy.js",
|
||||
"editor/js/ui/menu.js",
|
||||
"editor/js/ui/keyboard.js",
|
||||
"editor/js/ui/tabs.js",
|
||||
"editor/js/ui/popover.js",
|
||||
"editor/js/ui/workspaces.js",
|
||||
"editor/js/ui/view.js",
|
||||
"editor/js/ui/sidebar.js",
|
||||
"editor/js/ui/palette.js",
|
||||
"editor/js/ui/tab-info.js",
|
||||
"editor/js/ui/tab-config.js",
|
||||
"editor/js/ui/editor.js",
|
||||
"editor/js/ui/clipboard.js",
|
||||
"editor/js/ui/library.js",
|
||||
"editor/js/ui/notifications.js",
|
||||
"editor/js/ui/subflow.js",
|
||||
"editor/js/ui/touch/radialMenu.js",
|
||||
"editor/js/ui/typedInput.js"
|
||||
],
|
||||
dest: "public/red/red.js"
|
||||
},
|
||||
vendor: {
|
||||
files: {
|
||||
"public/vendor/vendor.js": [
|
||||
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
|
||||
"editor/vendor/bootstrap/js/bootstrap.min.js",
|
||||
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
|
||||
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"editor/vendor/marked/marked.min.js",
|
||||
"editor/vendor/orion/built-editor.min.js",
|
||||
"editor/vendor/d3/d3.v3.min.js",
|
||||
"editor/vendor/i18next/i18next.min.js"
|
||||
],
|
||||
"public/vendor/vendor.css": [
|
||||
"editor/vendor/orion/built-editor.css"
|
||||
// TODO: resolve relative resource paths in
|
||||
// bootstrap/FA/jquery
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
build: {
|
||||
files: {
|
||||
'public/red/red.min.js': 'public/red/red.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
build: {
|
||||
options: {
|
||||
outputStyle: 'compressed'
|
||||
},
|
||||
files: [{
|
||||
dest: 'public/red/style.min.css',
|
||||
src: 'editor/sass/style.scss'
|
||||
},
|
||||
{
|
||||
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
|
||||
src: 'editor/vendor/bootstrap/css/bootstrap.css'
|
||||
}]
|
||||
}
|
||||
},
|
||||
jsonlint: {
|
||||
messages: {
|
||||
src: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
]
|
||||
}
|
||||
},
|
||||
attachCopyright: {
|
||||
js: {
|
||||
src: [
|
||||
'public/red/red.min.js'
|
||||
]
|
||||
},
|
||||
css: {
|
||||
src: [
|
||||
'public/red/style.min.css'
|
||||
]
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
build: {
|
||||
src: [
|
||||
"public/red",
|
||||
"public/index.html",
|
||||
"public/favicon.ico",
|
||||
"public/icons",
|
||||
"public/vendor"
|
||||
]
|
||||
},
|
||||
release: {
|
||||
src: [
|
||||
'<%= paths.dist %>'
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
js: {
|
||||
files: [
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
tasks: ['concat','uglify','attachCopyright:js']
|
||||
},
|
||||
sass: {
|
||||
files: [
|
||||
'editor/sass/**/*.scss'
|
||||
],
|
||||
tasks: ['sass','attachCopyright:css']
|
||||
},
|
||||
json: {
|
||||
files: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
],
|
||||
tasks: ['jsonlint:messages']
|
||||
}
|
||||
},
|
||||
|
||||
nodemon: {
|
||||
/* uses .nodemonignore */
|
||||
dev: {
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'red','nodes'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
concurrent: {
|
||||
dev: {
|
||||
tasks: ['nodemon', 'watch'],
|
||||
options: {
|
||||
logConcurrentOutput: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
copy: {
|
||||
build: {
|
||||
files:[{
|
||||
cwd: 'editor/images',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'public/red/images/'
|
||||
},
|
||||
{
|
||||
cwd: 'editor/vendor',
|
||||
src: [
|
||||
'ace/**',
|
||||
//'bootstrap/css/**',
|
||||
'bootstrap/img/**',
|
||||
'jquery/css/**',
|
||||
'font-awesome/**'
|
||||
],
|
||||
expand: true,
|
||||
dest: 'public/vendor/'
|
||||
},
|
||||
{
|
||||
cwd: 'editor/icons',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'public/icons/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
src: ['editor/index.html','editor/favicon.ico'],
|
||||
dest: 'public/',
|
||||
flatten: true
|
||||
}]
|
||||
},
|
||||
release: {
|
||||
files: [{
|
||||
mode: true,
|
||||
expand: true,
|
||||
src: [
|
||||
'*.md',
|
||||
'LICENSE',
|
||||
'package.json',
|
||||
'settings.js',
|
||||
'red.js',
|
||||
'lib/.gitignore',
|
||||
'nodes/*.demo',
|
||||
'nodes/core/**',
|
||||
'red/**',
|
||||
'public/**',
|
||||
'editor/templates/**',
|
||||
'bin/**'
|
||||
],
|
||||
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
|
||||
}]
|
||||
}
|
||||
},
|
||||
chmod: {
|
||||
options: {
|
||||
mode: '755'
|
||||
},
|
||||
release: {
|
||||
// Target-specific file/dir lists and/or options go here.
|
||||
src: [
|
||||
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*')
|
||||
]
|
||||
}
|
||||
},
|
||||
compress: {
|
||||
release: {
|
||||
options: {
|
||||
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
|
||||
},
|
||||
expand: true,
|
||||
cwd: '<%= paths.dist %>/',
|
||||
src: ['node-red-<%= pkg.version %>/**']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
grunt.registerTask('default', ['test-core','test-editor','test-nodes']);
|
||||
|
||||
grunt.registerTask('test-core', ['jshint:core','simplemocha:core']);
|
||||
grunt.registerTask('test-editor', ['jshint:editor']);
|
||||
grunt.registerTask('test-nodes', ['simplemocha:nodes']);
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-nodemon');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-chmod');
|
||||
grunt.loadNpmTasks('grunt-jsonlint');
|
||||
|
||||
grunt.registerMultiTask('attachCopyright', function() {
|
||||
var files = this.data.src;
|
||||
var copyright = "/**\n"+
|
||||
" * Copyright 2013, 2015 IBM Corp.\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"+
|
||||
" * You may obtain a copy of the License at\n"+
|
||||
" *\n"+
|
||||
" * http://www.apache.org/licenses/LICENSE-2.0\n"+
|
||||
" *\n"+
|
||||
" * Unless required by applicable law or agreed to in writing, software\n"+
|
||||
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n"+
|
||||
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"+
|
||||
" * See the License for the specific language governing permissions and\n"+
|
||||
" * limitations under the License.\n"+
|
||||
" **/\n";
|
||||
|
||||
if (files) {
|
||||
for (var i=0;i<files.length;i++) {
|
||||
var file = files[i];
|
||||
if (!grunt.file.exists(file)) {
|
||||
grunt.log.warn('File '+ file + ' not found');
|
||||
return false;
|
||||
} else {
|
||||
var content = grunt.file.read(file);
|
||||
if (content.indexOf(copyright) == -1) {
|
||||
content = copyright+content;
|
||||
if (!grunt.file.write(file, content)) {
|
||||
return false;
|
||||
}
|
||||
grunt.log.writeln("Attached copyright to "+file);
|
||||
} else {
|
||||
grunt.log.writeln("Copyright already on "+file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('setDevEnv',
|
||||
'Sets NODE_ENV=development so non-minified assets are used',
|
||||
function () {
|
||||
process.env.NODE_ENV = 'development';
|
||||
});
|
||||
|
||||
grunt.registerTask('default',
|
||||
'Builds editor content then runs code style checks and unit tests on all components',
|
||||
['build','test-core','test-editor','test-nodes']);
|
||||
|
||||
grunt.registerTask('test-core',
|
||||
'Runs code style check and unit tests on core runtime code',
|
||||
['jshint:core','simplemocha:core']);
|
||||
|
||||
grunt.registerTask('test-editor',
|
||||
'Runs code style check on editor code',
|
||||
['jshint:editor']);
|
||||
|
||||
grunt.registerTask('test-nodes',
|
||||
'Runs unit tests on core nodes',
|
||||
['simplemocha:nodes']);
|
||||
|
||||
grunt.registerTask('build',
|
||||
'Builds editor content',
|
||||
['clean:build','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy:build','attachCopyright']);
|
||||
|
||||
grunt.registerTask('dev',
|
||||
'Developer mode: run node-red, watch for source changes and build/restart',
|
||||
['build','setDevEnv','concurrent:dev']);
|
||||
|
||||
grunt.registerTask('release',
|
||||
'Create distribution zip file',
|
||||
['build','clean:release','copy:release','chmod:release','compress:release']);
|
||||
|
||||
};
|
||||
|
57
INSTALL.md
@@ -1,57 +0,0 @@
|
||||
Node-RED Install
|
||||
================
|
||||
|
||||
## Install node.js
|
||||
|
||||
You can get the latest version from <http://nodejs.org/download/>.
|
||||
|
||||
Or, you may want to use a version from your operating system's package manager:
|
||||
<https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager>
|
||||
|
||||
## Get Node-RED
|
||||
|
||||
Clone the repository from GitHub:
|
||||
|
||||
$ git clone git@github.com:node-red/node-red.git
|
||||
|
||||
## Install the pre-requisite modules
|
||||
|
||||
From the top-level directory of Node-RED, run:
|
||||
|
||||
$ npm install
|
||||
|
||||
This will install the core pre-requisite modules.
|
||||
|
||||
## Run Node-RED
|
||||
|
||||
From the top-level directory, run:
|
||||
|
||||
$ node red.js
|
||||
|
||||
You can then access Node-RED at <http://localhost:1880>.
|
||||
|
||||
Online documentation is available at <http://nodered.org/docs>.
|
||||
|
||||
## Installing individual node dependencies
|
||||
|
||||
When Node-RED starts, it attempts to load the nodes from the `nodes/` directory.
|
||||
Each will have its own set of dependencies that will need to be installed before
|
||||
the node is available in the palette.
|
||||
|
||||
To help identify the dependencies, Node-RED logs any modules it fails to find
|
||||
for a particular node. You don't have to install these unless you want or need
|
||||
that node to appear.
|
||||
|
||||
Alternatively, a node's `.js` file can be examined to identify the modules it
|
||||
explicitly requires. For example, the Twitter node is defined in
|
||||
`nodes/social/27-twitter.js` and contains:
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var ntwitter = require('ntwitter');
|
||||
var OAuth= require('oauth').OAuth;
|
||||
|
||||
Of these, `ntwitter` and `oauth` are neither built-in modules nor ones provided
|
||||
by Node-RED itself. They can subsequently be installed by running:
|
||||
|
||||
$ npm install ntwitter oauth
|
||||
|
51
README.md
@@ -2,39 +2,58 @@
|
||||
|
||||
http://nodered.org
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red) [](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||

|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
Check out [INSTALL](INSTALL.md) for full instructions on getting started.
|
||||
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
started.
|
||||
|
||||
1. download the zip and unzip, or git clone
|
||||
2. cd node-red
|
||||
3. npm install
|
||||
4. node red.js
|
||||
5. Open <http://localhost:1880>
|
||||
1. `sudo npm install -g node-red`
|
||||
2. `node-red`
|
||||
3. Open <http://localhost:1880>
|
||||
|
||||
## Getting Help
|
||||
|
||||
More documentation can be found [here](http://nodered.org/docs).
|
||||
|
||||
For further help, or general discussion, please use the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
For further help, or general discussion, please use the
|
||||
[mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
|
||||
## Browser Support
|
||||
## Developers
|
||||
|
||||
The Node-RED editor runs in the browser. We routinely develop and test using
|
||||
Chrome and Firefox. We have anecdotal evidence that it works in recent versions of IE.
|
||||
If you want to run the latest code from git, here's how to get started:
|
||||
|
||||
We have basic support for using in mobile/tablet browsers.
|
||||
1. Install grunt, the build tool
|
||||
|
||||
npm install -g grunt-cli
|
||||
|
||||
2. Clone the code:
|
||||
|
||||
git clone https://github.com/node-red/node-red.git
|
||||
cd node-red
|
||||
|
||||
3. Install the node-red dependencies
|
||||
|
||||
npm install
|
||||
|
||||
4. Build the code
|
||||
|
||||
grunt build
|
||||
|
||||
5. Run
|
||||
|
||||
node red.js
|
||||
|
||||
## Contributing
|
||||
|
||||
Before raising a pull-request, please read our [contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
Before raising a pull-request, please read our
|
||||
[contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Authors
|
||||
|
||||
@@ -47,4 +66,4 @@ For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013, 2015 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
|
43
bin/node-red-pi
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2015 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Separate out node/v8 options from node-red ones
|
||||
OPTIONS=""
|
||||
ARGS=""
|
||||
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--userDir|--settings|--help) ARGS="$ARGS $arg";;
|
||||
--*) OPTIONS="$OPTIONS $arg";;
|
||||
*) ARGS="$ARGS $arg";;
|
||||
esac
|
||||
done
|
||||
|
||||
# Find the real location of this script
|
||||
CURRENT_PATH=`pwd`
|
||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
||||
while([ -h "${SCRIPT_PATH}" ]); do
|
||||
cd "`dirname "${SCRIPT_PATH}"`"
|
||||
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
||||
done
|
||||
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
|
||||
SCRIPT_PATH="`pwd`";
|
||||
cd $CURRENT_PATH
|
||||
|
||||
# Run Node-RED
|
||||
/usr/bin/env node $OPTIONS $SCRIPT_PATH/../red.js $ARGS
|
BIN
editor/favicon.ico
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 575 B |
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 601 B |
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 502 B After Width: | Height: | Size: 502 B |
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 671 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 736 B After Width: | Height: | Size: 736 B |
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 509 B |
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B |
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 392 B |
BIN
editor/images/grip.png
Normal file
After Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
33
editor/images/node-red-icon.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="480" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 480.00002 479.99999">
|
||||
<title>Node-RED Icon</title>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title>Node-RED Icon</dc:title>
|
||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by/3.0/"/>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Nick O'Leary</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
<cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/">
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="translate(0 -572.36)">
|
||||
<rect style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry="56" height="448" width="448" y="588.36" x="16" fill="#8f0000"/>
|
||||
<g transform="matrix(8.545 0 0 8.545 -786.19 -1949.8)">
|
||||
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m104.41 321.21c0.0138-2.3846-1.905-4.2806-4.2896-4.2806h-6.243v2.9257h6.243c0.80513 0 1.4808 0.58383 1.4808 1.389v4.2422c0 0.80513-0.67566 1.5075-1.4808 1.5075h-6.243v2.8086h6.243c2.3846 0 4.2895-1.9315 4.2895-4.3162l-0.00005-1.9812c9.8659 0.14125 12.737 2.7065 15.877 5.4519 3.0241 2.6446 6.4153 5.4869 15.252 5.557l0.00046 0.97238c0.001 2.3846 1.9543 4.3803 4.3389 4.3803h6.4273v-3.0427h-6.4273c-0.80514 0-1.4135-0.53255-1.4135-1.3377v-4.2422c0-0.80513 0.60835-1.4418 1.4135-1.4418h6.4273v-2.8086h-6.4273c-2.3846 0-4.3379 1.8658-4.3389 4.2504l-0.00045 1.005c-8.351-0.0276-10.723-2.3434-13.76-4.9992-2.5914-2.2662-5.6368-4.7578-12.346-5.6642 0.0583-0.0501 0.11211-0.0987 0.16838-0.15027 1.2918-1.1846 1.9884-2.6158 2.6699-3.8516 0.68148-1.2357 1.3227-2.267 2.373-2.9879 0.85207-0.58483 2.0639-1.0208 3.926-1.1017l0.00018 0.99192c0.00043 2.3846 1.9236 4.4325 4.3083 4.4325h17.242c2.3846 0 4.3127-2.0479 4.3127-4.4325v-4.2422c0-2.3846-1.9281-4.3153-4.3127-4.3153h-17.242c-2.3846 0-4.3095 1.9306-4.3083 4.3153l0.00051 0.98395c-2.2474 0.0903-3.9508 0.6357-5.2079 1.4985-1.5245 1.0464-2.3662 2.4764-3.0762 3.7637-0.70992 1.2873-1.3108 2.4408-2.2188 3.2734-0.79034 0.72475-1.8834 1.2844-3.658 1.493zm18.468-12.356h17.242c0.80514 0 1.387 0.58455 1.387 1.3897v4.2422c0 0.80514-0.5819 1.3898-1.387 1.3898h-17.242c-0.80514 0-1.4994-0.58462-1.4994-1.3898v-4.2422c0-0.80513 0.69431-1.3897 1.4994-1.3897z" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 1019 B |
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
BIN
editor/images/subflow_tab.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
editor/images/typedInput/09.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/az.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
editor/images/typedInput/bool.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
editor/images/typedInput/json.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
editor/images/typedInput/re.png
Normal file
After Width: | Height: | Size: 502 B |
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Copyright 2014, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,23 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.comms = (function() {
|
||||
|
||||
|
||||
var errornotification = null;
|
||||
var clearErrorTimer = null;
|
||||
|
||||
|
||||
var subscriptions = {};
|
||||
var ws;
|
||||
var pendingAuth = false;
|
||||
|
||||
var reconnectAttempts = 0;
|
||||
|
||||
function connectWS() {
|
||||
var path = location.hostname+":"+location.port+document.location.pathname;
|
||||
var path = location.hostname;
|
||||
var port = location.port;
|
||||
if (port.length !== 0) {
|
||||
path = path+":"+port;
|
||||
}
|
||||
path = path+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
|
||||
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
pendingAuth = (auth_tokens!=null);
|
||||
|
||||
|
||||
function completeConnection() {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
@@ -37,9 +44,10 @@ RED.comms = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
reconnectAttempts = 0;
|
||||
if (errornotification) {
|
||||
clearErrorTimer = setTimeout(function() {
|
||||
errornotification.close();
|
||||
@@ -74,16 +82,17 @@ RED.comms = (function() {
|
||||
}
|
||||
};
|
||||
ws.onclose = function() {
|
||||
if (errornotification == null) {
|
||||
errornotification = RED.notify("<b>Error</b>: Lost connection to server","error",true);
|
||||
if (reconnectAttempts > 5 && errornotification == null) {
|
||||
errornotification = RED.notify(RED._("notification.error",{message:RED._("notification.errors.lostConnection")}),"error",true);
|
||||
} else if (clearErrorTimer) {
|
||||
clearTimeout(clearErrorTimer);
|
||||
clearErrorTimer = null;
|
||||
}
|
||||
reconnectAttempts++;
|
||||
setTimeout(connectWS,1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function subscribe(topic,callback) {
|
||||
if (subscriptions[topic] == null) {
|
||||
subscriptions[topic] = [];
|
||||
@@ -93,7 +102,7 @@ RED.comms = (function() {
|
||||
ws.send(JSON.stringify({subscribe:topic}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function unsubscribe(topic,callback) {
|
||||
if (subscriptions[topic]) {
|
||||
for (var i=0;i<subscriptions[topic].length;i++) {
|
||||
@@ -107,7 +116,7 @@ RED.comms = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
connect: connectWS,
|
||||
subscribe: subscribe,
|
48
editor/js/events.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.events = (function() {
|
||||
var handlers = {};
|
||||
|
||||
function on(evt,func) {
|
||||
handlers[evt] = handlers[evt]||[];
|
||||
handlers[evt].push(func);
|
||||
}
|
||||
function off(evt,func) {
|
||||
var handler = handlers[evt];
|
||||
if (handler) {
|
||||
for (var i=0;i<handler.length;i++) {
|
||||
if (handler[i] === func) {
|
||||
handler.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function emit(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
handlers[evt][i](arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return {
|
||||
on: on,
|
||||
off: off,
|
||||
emit: emit
|
||||
}
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
**/
|
||||
RED.history = (function() {
|
||||
var undo_history = [];
|
||||
|
||||
|
||||
return {
|
||||
//TODO: this function is a placeholder until there is a 'save' event that can be listened to
|
||||
markAllDirty: function() {
|
||||
@@ -32,10 +32,17 @@ RED.history = (function() {
|
||||
pop: function() {
|
||||
var ev = undo_history.pop();
|
||||
var i;
|
||||
var node;
|
||||
var subflow;
|
||||
var modifiedTabs = {};
|
||||
if (ev) {
|
||||
if (ev.t == 'add') {
|
||||
if (ev.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
node = RED.nodes.node(ev.nodes[i]);
|
||||
if (node.z) {
|
||||
modifiedTabs[node.z] = true;
|
||||
}
|
||||
RED.nodes.remove(ev.nodes[i]);
|
||||
}
|
||||
}
|
||||
@@ -47,26 +54,48 @@ RED.history = (function() {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.removeWorkspace(ev.workspaces[i].id);
|
||||
RED.view.removeWorkspace(ev.workspaces[i]);
|
||||
RED.workspaces.remove(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflows) {
|
||||
for (i=0;i<ev.subflows.length;i++) {
|
||||
RED.nodes.removeSubflow(ev.subflows[i]);
|
||||
RED.view.removeWorkspace(ev.subflows[i]);
|
||||
RED.workspaces.remove(ev.subflows[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.instances) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('changed')) {
|
||||
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||
if (subflow) {
|
||||
subflow.changed = ev.subflow.changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.addWorkspace(ev.workspaces[i]);
|
||||
RED.view.addWorkspace(ev.workspaces[i]);
|
||||
RED.workspaces.add(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow);
|
||||
if (ev.subflow && ev.subflow.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow.subflow);
|
||||
}
|
||||
var subflow;
|
||||
if (ev.subflowInputs && ev.subflowInputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowInputs[0].z);
|
||||
subflow.in.push(ev.subflowInputs[0]);
|
||||
@@ -91,23 +120,30 @@ RED.history = (function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+subflow.id) {
|
||||
n.changed = true;
|
||||
n.inputs = subflow.in.length;
|
||||
n.outputs = subflow.out.length;
|
||||
while (n.outputs > n.ports.length) {
|
||||
n.ports.push(n.ports.length);
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
n.inputs = subflow.in.length;
|
||||
n.outputs = subflow.out.length;
|
||||
while (n.outputs > n.ports.length) {
|
||||
n.ports.push(n.ports.length);
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
});
|
||||
}
|
||||
if (ev.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.add(ev.nodes[i]);
|
||||
modifiedTabs[ev.nodes[i].z] = true;
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
@@ -115,6 +151,22 @@ RED.history = (function() {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.changes) {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
node = RED.nodes.node(i);
|
||||
if (node) {
|
||||
for (var d in ev.changes[i]) {
|
||||
if (ev.changes[i].hasOwnProperty(d)) {
|
||||
node[d] = ev.changes[i][d];
|
||||
}
|
||||
}
|
||||
node.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ev.t == "move") {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
var n = ev.nodes[i];
|
||||
@@ -122,9 +174,31 @@ RED.history = (function() {
|
||||
n.n.y = n.oy;
|
||||
n.n.dirty = true;
|
||||
}
|
||||
// A move could have caused a link splice
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
if (ev.node._def.defaults[i].type) {
|
||||
// This is a config node property
|
||||
var currentConfigNode = RED.nodes.node(ev.node[i]);
|
||||
if (currentConfigNode) {
|
||||
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
|
||||
}
|
||||
var newConfigNode = RED.nodes.node(ev.changes[i]);
|
||||
if (newConfigNode) {
|
||||
newConfigNode.users.push(ev.node);
|
||||
}
|
||||
}
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
@@ -143,16 +217,24 @@ RED.history = (function() {
|
||||
ev.node.out = ev.node.out.concat(ev.subflow.outputs);
|
||||
}
|
||||
}
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+ev.node.id) {
|
||||
n.changed = ev.changed;
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
|
||||
RED.palette.refresh();
|
||||
|
||||
if (ev.node.type === 'subflow') {
|
||||
$("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(ev.node.name);
|
||||
}
|
||||
} else {
|
||||
RED.editor.updateNodeProperties(ev.node);
|
||||
RED.editor.validateNode(ev.node);
|
||||
@@ -166,11 +248,9 @@ RED.history = (function() {
|
||||
ev.node.changed = ev.changed;
|
||||
} else if (ev.t == "createSubflow") {
|
||||
if (ev.nodes) {
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.z === ev.subflow.id) {
|
||||
n.z = ev.activeWorkspace;
|
||||
n.dirty = true;
|
||||
}
|
||||
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
||||
n.z = ev.activeWorkspace;
|
||||
n.dirty = true;
|
||||
});
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.remove(ev.nodes[i]);
|
||||
@@ -181,18 +261,28 @@ RED.history = (function() {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow);
|
||||
RED.view.removeWorkspace(ev.subflow);
|
||||
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow.subflow);
|
||||
RED.workspaces.remove(ev.subflow.subflow);
|
||||
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.view.dirty(ev.dirty);
|
||||
RED.view.redraw();
|
||||
Object.keys(modifiedTabs).forEach(function(id) {
|
||||
var subflow = RED.nodes.subflow(id);
|
||||
if (subflow) {
|
||||
RED.editor.validateNode(subflow);
|
||||
}
|
||||
});
|
||||
|
||||
RED.nodes.dirty(ev.dirty);
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
}
|
||||
}
|
43
editor/js/i18n.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.i18n = (function() {
|
||||
|
||||
return {
|
||||
init: function(done) {
|
||||
i18n.init({
|
||||
resGetPath: 'locales/__ns__',
|
||||
dynamicLoad: false,
|
||||
load:'current',
|
||||
ns: {
|
||||
namespaces: ["editor","node-red"],
|
||||
defaultNs: "editor"
|
||||
},
|
||||
fallbackLng: ['en-US'],
|
||||
useCookie: false
|
||||
},function() {
|
||||
done();
|
||||
});
|
||||
RED["_"] = function() {
|
||||
return i18n.t.apply(null,arguments);
|
||||
}
|
||||
|
||||
},
|
||||
loadCatalog: function(namespace,done) {
|
||||
i18n.loadNamespace(namespace,done);
|
||||
}
|
||||
}
|
||||
})();
|
235
editor/js/main.js
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var RED = (function() {
|
||||
|
||||
|
||||
function loadNodeList() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
RED.nodes.setNodeList(data);
|
||||
|
||||
var nsCount = 0;
|
||||
for(var i=0;i<data.length;i++) {
|
||||
var ns = data[i];
|
||||
if (ns.module != "node-red") {
|
||||
nsCount++;
|
||||
RED.i18n.loadCatalog(ns.id, function() {
|
||||
nsCount--;
|
||||
if (nsCount === 0) {
|
||||
loadNodes();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (nsCount === 0) {
|
||||
loadNodes();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadNodes() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"text/html"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
$("body").append(data);
|
||||
$("body").i18n();
|
||||
|
||||
|
||||
$(".palette-spinner").hide();
|
||||
$(".palette-scroll").show();
|
||||
$("#palette-search").show();
|
||||
loadFlows();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadFlows() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'flows',
|
||||
success: function(nodes) {
|
||||
RED.nodes.import(nodes);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
RED.comms.subscribe("status/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var node = RED.nodes.node(parts[1]);
|
||||
if (node) {
|
||||
if (msg.text) {
|
||||
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
|
||||
}
|
||||
node.status = msg;
|
||||
if (statusEnabled) {
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("node/#",function(topic,msg) {
|
||||
var i,m;
|
||||
var typeList;
|
||||
var info;
|
||||
|
||||
if (topic == "node/added") {
|
||||
var addedTypes = [];
|
||||
for (i=0;i<msg.length;i++) {
|
||||
m = msg[i];
|
||||
var id = m.id;
|
||||
RED.nodes.addNodeSet(m);
|
||||
addedTypes = addedTypes.concat(m.types);
|
||||
$.get('nodes/'+id, function(data) {
|
||||
$("body").append(data);
|
||||
});
|
||||
}
|
||||
if (addedTypes.length) {
|
||||
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
|
||||
}
|
||||
} else if (topic == "node/removed") {
|
||||
for (i=0;i<msg.length;i++) {
|
||||
m = msg[i];
|
||||
info = RED.nodes.removeNodeSet(m.id);
|
||||
if (info.added) {
|
||||
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
||||
}
|
||||
}
|
||||
} else if (topic == "node/enabled") {
|
||||
if (msg.types) {
|
||||
info = RED.nodes.getNodeSet(msg.id);
|
||||
if (info.added) {
|
||||
RED.nodes.enableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
|
||||
} else {
|
||||
$.get('nodes/'+msg.id, function(data) {
|
||||
$("body").append(data);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (topic == "node/disabled") {
|
||||
if (msg.types) {
|
||||
RED.nodes.disableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var statusEnabled = false;
|
||||
function toggleStatus(state) {
|
||||
statusEnabled = state;
|
||||
RED.view.status(statusEnabled);
|
||||
}
|
||||
|
||||
function loadEditor() {
|
||||
RED.menu.init({id:"btn-sidemenu",
|
||||
options: [
|
||||
{id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||
{id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid},
|
||||
{id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid},
|
||||
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true},
|
||||
null,
|
||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-import",label:RED._("menu.label.import"),options:[
|
||||
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import},
|
||||
{id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
|
||||
]},
|
||||
{id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[
|
||||
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export},
|
||||
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export}
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function(){}},
|
||||
{id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
||||
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add},
|
||||
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit},
|
||||
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove},
|
||||
null
|
||||
]},
|
||||
{id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow},
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp},
|
||||
{id:"menu-item-help",
|
||||
label: RED.settings.theme("menu.menu-item-help.label","Node-RED Website"),
|
||||
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
|
||||
},
|
||||
{id:"menu-item-node-red-version", label:"v"+RED.settings.version}
|
||||
]
|
||||
});
|
||||
|
||||
RED.user.init();
|
||||
|
||||
RED.library.init();
|
||||
RED.palette.init();
|
||||
RED.sidebar.init();
|
||||
RED.subflow.init();
|
||||
RED.workspaces.init();
|
||||
RED.clipboard.init();
|
||||
RED.view.init();
|
||||
RED.editor.init();
|
||||
|
||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||
|
||||
RED.keyboard.add(/* ? */ 191,{shift:true},function(){RED.keyboard.showHelp();d3.event.preventDefault();});
|
||||
RED.comms.connect();
|
||||
|
||||
$("#main-container").show();
|
||||
$(".header-toolbar").show();
|
||||
|
||||
loadNodeList();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
||||
document.title = document.title+" : "+window.location.hostname;
|
||||
}
|
||||
|
||||
ace.require("ace/ext/language_tools");
|
||||
|
||||
RED.i18n.init(function() {
|
||||
RED.settings.init(loadEditor);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
};
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,13 +22,20 @@ RED.nodes = (function() {
|
||||
var defaultWorkspace;
|
||||
var workspaces = {};
|
||||
var subflows = {};
|
||||
|
||||
|
||||
var dirty = false;
|
||||
|
||||
function setDirty(d) {
|
||||
dirty = d;
|
||||
RED.events.emit("nodes:change",{dirty:dirty});
|
||||
}
|
||||
|
||||
var registry = (function() {
|
||||
var nodeList = [];
|
||||
var nodeSets = {};
|
||||
var typeToId = {};
|
||||
var nodeDefinitions = {};
|
||||
|
||||
|
||||
var exports = {
|
||||
getNodeList: function() {
|
||||
return nodeList;
|
||||
@@ -100,7 +107,23 @@ RED.nodes = (function() {
|
||||
registerNodeType: function(nt,def) {
|
||||
nodeDefinitions[nt] = def;
|
||||
if (def.category != "subflows") {
|
||||
def.set = nodeSets[typeToId[nt]];
|
||||
nodeSets[typeToId[nt]].added = true;
|
||||
|
||||
var ns;
|
||||
if (def.set.module === "node-red") {
|
||||
ns = "node-red";
|
||||
} else {
|
||||
ns = def.set.id;
|
||||
}
|
||||
def["_"] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
if (args[0].indexOf(":") === -1) {
|
||||
args[0] = ns+":"+args[0];
|
||||
}
|
||||
return RED._.apply(null,args);
|
||||
}
|
||||
|
||||
// TODO: too tightly coupled into palette UI
|
||||
}
|
||||
RED.palette.add(nt,def);
|
||||
@@ -110,6 +133,7 @@ RED.nodes = (function() {
|
||||
},
|
||||
removeNodeType: function(nt) {
|
||||
if (nt.substring(0,8) != "subflow:") {
|
||||
// NON-NLS - internal debug message
|
||||
throw new Error("this api is subflow only. called with:",nt);
|
||||
}
|
||||
delete nodeDefinitions[nt];
|
||||
@@ -121,16 +145,24 @@ RED.nodes = (function() {
|
||||
};
|
||||
return exports;
|
||||
})();
|
||||
|
||||
|
||||
function getID() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
function addNode(n) {
|
||||
if (n.type.indexOf("subflow") !== 0) {
|
||||
n["_"] = n._def._;
|
||||
}
|
||||
if (n._def.category == "config") {
|
||||
configNodes[n.id] = n;
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
n.ports = [];
|
||||
if (n.outputs) {
|
||||
for (var i=0;i<n.outputs;i++) {
|
||||
n.ports.push(i);
|
||||
}
|
||||
}
|
||||
n.dirty = true;
|
||||
var updatedConfigNode = false;
|
||||
for (var d in n._def.defaults) {
|
||||
@@ -149,7 +181,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
// TODO: refresh config tab?
|
||||
}
|
||||
if (n._def.category == "subflows" && typeof n.i === "undefined") {
|
||||
var nextId = 0;
|
||||
@@ -160,13 +192,13 @@ RED.nodes = (function() {
|
||||
}
|
||||
nodes.push(n);
|
||||
}
|
||||
if (n._def.onadd) {
|
||||
n._def.onadd.call(n);
|
||||
}
|
||||
}
|
||||
function addLink(l) {
|
||||
links.push(l);
|
||||
}
|
||||
function addConfig(c) {
|
||||
configNodes[c.id] = c;
|
||||
}
|
||||
|
||||
function getNode(id) {
|
||||
if (id in configNodes) {
|
||||
@@ -183,11 +215,14 @@ RED.nodes = (function() {
|
||||
|
||||
function removeNode(id) {
|
||||
var removedLinks = [];
|
||||
var removedNodes = [];
|
||||
var node;
|
||||
if (id in configNodes) {
|
||||
node = configNodes[id];
|
||||
delete configNodes[id];
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
} else {
|
||||
var node = getNode(id);
|
||||
node = getNode(id);
|
||||
if (node) {
|
||||
nodes.splice(nodes.indexOf(node),1);
|
||||
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
||||
@@ -202,19 +237,27 @@ RED.nodes = (function() {
|
||||
var configNode = configNodes[node[d]];
|
||||
if (configNode) {
|
||||
updatedConfigNode = true;
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(node),1);
|
||||
if (configNode._def.exclusive) {
|
||||
removeNode(node[d]);
|
||||
removedNodes.push(configNode);
|
||||
} else {
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(node),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
return removedLinks;
|
||||
if (node && node._def.onremove) {
|
||||
node._def.onremove.call(n);
|
||||
}
|
||||
return {links:removedLinks,nodes:removedNodes};
|
||||
}
|
||||
|
||||
function removeLink(l) {
|
||||
@@ -224,12 +267,6 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function refreshValidation() {
|
||||
for (var n=0;n<nodes.length;n++) {
|
||||
RED.editor.validateNode(nodes[n]);
|
||||
}
|
||||
}
|
||||
|
||||
function addWorkspace(ws) {
|
||||
workspaces[ws.id] = ws;
|
||||
}
|
||||
@@ -241,23 +278,50 @@ RED.nodes = (function() {
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var n;
|
||||
var node;
|
||||
for (n=0;n<nodes.length;n++) {
|
||||
var node = nodes[n];
|
||||
node = nodes[n];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
for(n in configNodes) {
|
||||
if (configNodes.hasOwnProperty(n)) {
|
||||
node = configNodes[n];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (n=0;n<removedNodes.length;n++) {
|
||||
var rmlinks = removeNode(removedNodes[n].id);
|
||||
removedLinks = removedLinks.concat(rmlinks);
|
||||
var result = removeNode(removedNodes[n].id);
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
return {nodes:removedNodes,links:removedLinks};
|
||||
}
|
||||
|
||||
function addSubflow(sf) {
|
||||
function addSubflow(sf, createNewIds) {
|
||||
if (createNewIds) {
|
||||
var subflowNames = Object.keys(subflows).map(function(sfid) {
|
||||
return subflows[sfid].name;
|
||||
});
|
||||
|
||||
subflowNames.sort();
|
||||
var copyNumber = 1;
|
||||
var subflowName = sf.name;
|
||||
subflowNames.forEach(function(name) {
|
||||
if (subflowName == name) {
|
||||
copyNumber++;
|
||||
subflowName = sf.name+" ("+copyNumber+")";
|
||||
}
|
||||
});
|
||||
sf.name = subflowName;
|
||||
}
|
||||
|
||||
subflows[sf.id] = sf;
|
||||
RED.nodes.registerType("subflow:"+sf.id, {
|
||||
defaults:{name:{value:""}},
|
||||
info: sf.info,
|
||||
icon:"subflow.png",
|
||||
category: "subflows",
|
||||
inputs: sf.in.length,
|
||||
@@ -265,10 +329,13 @@ RED.nodes = (function() {
|
||||
color: "#da9",
|
||||
label: function() { return this.name||RED.nodes.subflow(sf.id).name },
|
||||
labelStyle: function() { return this.name?"node_label_italic":""; },
|
||||
paletteLabel: function() { return RED.nodes.subflow(sf.id).name }
|
||||
paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
|
||||
set:{
|
||||
module: "node-red"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
function getSubflow(id) {
|
||||
return subflows[id];
|
||||
@@ -277,7 +344,7 @@ RED.nodes = (function() {
|
||||
delete subflows[sf.id];
|
||||
registry.removeNodeType("subflow:"+sf.id);
|
||||
}
|
||||
|
||||
|
||||
function subflowContains(sfid,nodeid) {
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
@@ -297,7 +364,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function getAllFlowNodes(node) {
|
||||
var visited = {};
|
||||
visited[node.id] = true;
|
||||
@@ -330,6 +397,7 @@ RED.nodes = (function() {
|
||||
var node = {};
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.z = n.z;
|
||||
if (node.type == "unknown") {
|
||||
for (var p in n._orig) {
|
||||
if (n._orig.hasOwnProperty(p)) {
|
||||
@@ -365,7 +433,6 @@ RED.nodes = (function() {
|
||||
if (n._def.category != "config") {
|
||||
node.x = n.x;
|
||||
node.y = n.y;
|
||||
node.z = n.z;
|
||||
node.wires = [];
|
||||
for(var i=0;i<n.outputs;i++) {
|
||||
node.wires.push([]);
|
||||
@@ -386,9 +453,10 @@ RED.nodes = (function() {
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.name = n.name;
|
||||
node.info = n.info;
|
||||
node.in = [];
|
||||
node.out = [];
|
||||
|
||||
|
||||
n.in.forEach(function(p) {
|
||||
var nIn = {x:p.x,y:p.y,wires:[]};
|
||||
var wires = links.filter(function(d) { return d.source === p });
|
||||
@@ -412,8 +480,8 @@ RED.nodes = (function() {
|
||||
}
|
||||
node.out.push(nOut);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
@@ -424,16 +492,16 @@ RED.nodes = (function() {
|
||||
var exportedConfigNodes = {};
|
||||
var exportedSubflows = {};
|
||||
for (var n=0;n<set.length;n++) {
|
||||
var node = set[n].n;
|
||||
var node = set[n];
|
||||
if (node.type.substring(0,8) == "subflow:") {
|
||||
var subflowId = node.type.substring(8);
|
||||
if (!exportedSubflows[subflowId]) {
|
||||
exportedSubflows[subflowId] = true;
|
||||
var subflow = getSubflow(subflowId);
|
||||
var subflowSet = [{n:subflow}];
|
||||
var subflowSet = [subflow];
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.z == subflowId) {
|
||||
subflowSet.push({n:n});
|
||||
subflowSet.push(n);
|
||||
}
|
||||
});
|
||||
var exportableSubflow = createExportableNodeSet(subflowSet);
|
||||
@@ -493,231 +561,308 @@ RED.nodes = (function() {
|
||||
return nns;
|
||||
}
|
||||
|
||||
function compareNodes(nodeA,nodeB,idMustMatch) {
|
||||
if (idMustMatch && nodeA.id != nodeB.id) {
|
||||
return false;
|
||||
}
|
||||
if (nodeA.type != nodeB.type) {
|
||||
return false;
|
||||
}
|
||||
var def = nodeA._def;
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
var vA = nodeA[d];
|
||||
var vB = nodeB[d];
|
||||
if (typeof vA !== typeof vB) {
|
||||
return false;
|
||||
}
|
||||
if (vA === null || typeof vA === "string" || typeof vA === "number") {
|
||||
if (vA !== vB) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (JSON.stringify(vA) !== JSON.stringify(vB)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function importNodes(newNodesObj,createNewIds) {
|
||||
try {
|
||||
var i;
|
||||
var n;
|
||||
var newNodes;
|
||||
if (typeof newNodesObj === "string") {
|
||||
if (newNodesObj === "") {
|
||||
return;
|
||||
}
|
||||
var i;
|
||||
var n;
|
||||
var newNodes;
|
||||
if (typeof newNodesObj === "string") {
|
||||
if (newNodesObj === "") {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
newNodes = JSON.parse(newNodesObj);
|
||||
} else {
|
||||
newNodes = newNodesObj;
|
||||
} catch(err) {
|
||||
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
|
||||
e.code = "NODE_RED";
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
newNodes = newNodesObj;
|
||||
}
|
||||
|
||||
if (!$.isArray(newNodes)) {
|
||||
newNodes = [newNodes];
|
||||
if (!$.isArray(newNodes)) {
|
||||
newNodes = [newNodes];
|
||||
}
|
||||
var unknownTypes = [];
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type != "workspace" &&
|
||||
n.type != "tab" &&
|
||||
n.type != "subflow" &&
|
||||
!registry.getNodeType(n.type) &&
|
||||
n.type.substring(0,8) != "subflow:" &&
|
||||
unknownTypes.indexOf(n.type)==-1) {
|
||||
unknownTypes.push(n.type);
|
||||
}
|
||||
var unknownTypes = [];
|
||||
}
|
||||
if (unknownTypes.length > 0) {
|
||||
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
|
||||
var type = "type"+(unknownTypes.length > 1?"s":"");
|
||||
RED.notify("<strong>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</strong>"+typeList,"error",false,10000);
|
||||
}
|
||||
|
||||
var activeWorkspace = RED.workspaces.active();
|
||||
var activeSubflow = getSubflow(activeWorkspace);
|
||||
if (activeSubflow) {
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type != "workspace" &&
|
||||
n.type != "tab" &&
|
||||
n.type != "subflow" &&
|
||||
!registry.getNodeType(n.type) &&
|
||||
n.type.substring(0,8) != "subflow:") {
|
||||
// TODO: get this UI thing out of here! (see below as well)
|
||||
|
||||
if (unknownTypes.indexOf(n.type)==-1) {
|
||||
unknownTypes.push(n.type);
|
||||
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
|
||||
if (m) {
|
||||
var subflowId = m[1];
|
||||
var err;
|
||||
if (subflowId === activeSubflow.id) {
|
||||
err = new Error(RED._("notification.errors.cannotAddSubflowToItself"));
|
||||
}
|
||||
//if (n.x == null && n.y == null) {
|
||||
// // config node - remove it
|
||||
// newNodes.splice(i,1);
|
||||
// i--;
|
||||
//}
|
||||
}
|
||||
}
|
||||
if (unknownTypes.length > 0) {
|
||||
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
|
||||
var type = "type"+(unknownTypes.length > 1?"s":"");
|
||||
RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
|
||||
//"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
|
||||
}
|
||||
|
||||
var activeWorkspace = RED.view.getWorkspace();
|
||||
var activeSubflow = getSubflow(activeWorkspace);
|
||||
if (activeSubflow) {
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
|
||||
if (m) {
|
||||
var subflowId = m[1];
|
||||
var err;
|
||||
if (subflowId === activeSubflow.id) {
|
||||
err = new Error("Cannot add subflow to itself");
|
||||
}
|
||||
if (subflowContains(m[1],activeSubflow.id)) {
|
||||
err = new Error("Cannot add subflow - circular reference detected");
|
||||
}
|
||||
if (err) {
|
||||
// TODO: standardise error codes
|
||||
err.code = "NODE_RED";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (subflowContains(m[1],activeSubflow.id)) {
|
||||
err = new Error(RED._("notification.errors.cannotAddCircularReference"));
|
||||
}
|
||||
if (err) {
|
||||
// TODO: standardise error codes
|
||||
err.code = "NODE_RED";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var new_workspaces = [];
|
||||
var workspace_map = {};
|
||||
var new_subflows = [];
|
||||
var subflow_map = {};
|
||||
var nid;
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type === "workspace" || n.type === "tab") {
|
||||
if (n.type === "workspace") {
|
||||
n.type = "tab";
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = n;
|
||||
}
|
||||
if (createNewIds) {
|
||||
nid = getID();
|
||||
workspace_map[n.id] = nid;
|
||||
n.id = nid;
|
||||
}
|
||||
addWorkspace(n);
|
||||
RED.view.addWorkspace(n);
|
||||
new_workspaces.push(n);
|
||||
} else if (n.type === "subflow") {
|
||||
subflow_map[n.id] = n;
|
||||
if (createNewIds) {
|
||||
nid = getID();
|
||||
n.id = nid;
|
||||
}
|
||||
// TODO: handle createNewIds - map old to new subflow ids
|
||||
n.in.forEach(function(input,i) {
|
||||
input.type = "subflow";
|
||||
input.direction = "in";
|
||||
input.z = n.id;
|
||||
input.i = i;
|
||||
input.id = getID();
|
||||
});
|
||||
n.out.forEach(function(output,i) {
|
||||
output.type = "subflow";
|
||||
output.direction = "out";
|
||||
output.z = n.id;
|
||||
output.i = i;
|
||||
output.id = getID();
|
||||
});
|
||||
new_subflows.push(n);
|
||||
addSubflow(n);
|
||||
}
|
||||
|
||||
var new_workspaces = [];
|
||||
var workspace_map = {};
|
||||
var new_subflows = [];
|
||||
var subflow_map = {};
|
||||
var node_map = {};
|
||||
var new_nodes = [];
|
||||
var new_links = [];
|
||||
var nid;
|
||||
var def;
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type === "workspace" || n.type === "tab") {
|
||||
if (n.type === "workspace") {
|
||||
n.type = "tab";
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = n;
|
||||
}
|
||||
if (createNewIds) {
|
||||
nid = getID();
|
||||
workspace_map[n.id] = nid;
|
||||
n.id = nid;
|
||||
}
|
||||
addWorkspace(n);
|
||||
RED.workspaces.add(n);
|
||||
new_workspaces.push(n);
|
||||
} else if (n.type === "subflow") {
|
||||
subflow_map[n.id] = n;
|
||||
if (createNewIds) {
|
||||
nid = getID();
|
||||
n.id = nid;
|
||||
}
|
||||
// TODO: handle createNewIds - map old to new subflow ids
|
||||
n.in.forEach(function(input,i) {
|
||||
input.type = "subflow";
|
||||
input.direction = "in";
|
||||
input.z = n.id;
|
||||
input.i = i;
|
||||
input.id = getID();
|
||||
});
|
||||
n.out.forEach(function(output,i) {
|
||||
output.type = "subflow";
|
||||
output.direction = "out";
|
||||
output.z = n.id;
|
||||
output.i = i;
|
||||
output.id = getID();
|
||||
});
|
||||
new_subflows.push(n);
|
||||
addSubflow(n,createNewIds);
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
|
||||
addWorkspace(defaultWorkspace);
|
||||
RED.view.addWorkspace(defaultWorkspace);
|
||||
new_workspaces.push(defaultWorkspace);
|
||||
activeWorkspace = RED.view.getWorkspace();
|
||||
}
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})};
|
||||
addWorkspace(defaultWorkspace);
|
||||
RED.workspaces.add(defaultWorkspace);
|
||||
new_workspaces.push(defaultWorkspace);
|
||||
activeWorkspace = RED.workspaces.active();
|
||||
}
|
||||
|
||||
var node_map = {};
|
||||
var new_nodes = [];
|
||||
var new_links = [];
|
||||
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
|
||||
var def = registry.getNodeType(n.type);
|
||||
if (def && def.category == "config") {
|
||||
if (!RED.nodes.node(n.id)) {
|
||||
var configNode = {id:n.id,type:n.type,users:[]};
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
configNode[d] = n[d];
|
||||
}
|
||||
}
|
||||
configNode.label = def.label;
|
||||
configNode._def = def;
|
||||
RED.nodes.add(configNode);
|
||||
}
|
||||
} else {
|
||||
var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
|
||||
if (createNewIds) {
|
||||
if (subflow_map[node.z]) {
|
||||
node.z = subflow_map[node.z].id;
|
||||
} else {
|
||||
node.z = workspace_map[node.z];
|
||||
if (!workspaces[node.z]) {
|
||||
node.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
node.id = getID();
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
def = registry.getNodeType(n.type);
|
||||
if (def && def.category == "config") {
|
||||
var existingConfigNode = null;
|
||||
if (createNewIds) {
|
||||
if (n.z) {
|
||||
if (subflow_map[n.z]) {
|
||||
n.z = subflow_map[n.z].id;
|
||||
} else {
|
||||
node.id = n.id;
|
||||
if (node.z == null || (!workspaces[node.z] && !subflow_map[node.z])) {
|
||||
n.z = workspace_map[n.z];
|
||||
if (!workspaces[n.z]) {
|
||||
n.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
existingConfigNode = RED.nodes.node(n.id);
|
||||
if (existingConfigNode) {
|
||||
if (n.z && existingConfigNode.z !== n.z) {
|
||||
existingConfigNode = null;
|
||||
// Check the config nodes on n.z
|
||||
for (var cn in configNodes) {
|
||||
if (configNodes.hasOwnProperty(cn)) {
|
||||
if (configNodes[cn].z === n.z && compareNodes(configNodes[cn],n,false)) {
|
||||
existingConfigNode = configNodes[cn];
|
||||
node_map[n.id] = configNodes[cn];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) {
|
||||
var configNode = {id:n.id, z:n.z, type:n.type, users:[]};
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
configNode[d] = n[d];
|
||||
}
|
||||
}
|
||||
configNode.label = def.label;
|
||||
configNode._def = def;
|
||||
if (createNewIds) {
|
||||
configNode.id = getID();
|
||||
}
|
||||
node_map[n.id] = configNode;
|
||||
new_nodes.push(configNode);
|
||||
RED.nodes.add(configNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
|
||||
def = registry.getNodeType(n.type);
|
||||
if (!def || def.category != "config") {
|
||||
var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
|
||||
if (createNewIds) {
|
||||
if (subflow_map[node.z]) {
|
||||
node.z = subflow_map[node.z].id;
|
||||
} else {
|
||||
node.z = workspace_map[node.z];
|
||||
if (!workspaces[node.z]) {
|
||||
node.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
node.type = n.type;
|
||||
node._def = def;
|
||||
if (n.type.substring(0,7) === "subflow") {
|
||||
var parentId = n.type.split(":")[1];
|
||||
var subflow = subflow_map[parentId]||getSubflow(parentId);
|
||||
if (createNewIds) {
|
||||
parentId = subflow.id;
|
||||
node.type = "subflow:"+parentId;
|
||||
node._def = registry.getNodeType(node.type);
|
||||
delete node.i;
|
||||
}
|
||||
node.name = n.name;
|
||||
node.outputs = subflow.out.length;
|
||||
node.inputs = subflow.in.length;
|
||||
} else {
|
||||
if (!node._def) {
|
||||
if (node.x && node.y) {
|
||||
node._def = {
|
||||
color:"#fee",
|
||||
defaults: {},
|
||||
label: "unknown: "+n.type,
|
||||
labelStyle: "node_label_italic",
|
||||
outputs: n.outputs||n.wires.length
|
||||
}
|
||||
} else {
|
||||
node._def = {
|
||||
category:"config"
|
||||
};
|
||||
node.users = [];
|
||||
node.id = getID();
|
||||
} else {
|
||||
node.id = n.id;
|
||||
if (node.z == null || (!workspaces[node.z] && !subflow_map[node.z])) {
|
||||
node.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
node.type = n.type;
|
||||
node._def = def;
|
||||
if (n.type.substring(0,7) === "subflow") {
|
||||
var parentId = n.type.split(":")[1];
|
||||
var subflow = subflow_map[parentId]||getSubflow(parentId);
|
||||
if (createNewIds) {
|
||||
parentId = subflow.id;
|
||||
node.type = "subflow:"+parentId;
|
||||
node._def = registry.getNodeType(node.type);
|
||||
delete node.i;
|
||||
}
|
||||
node.name = n.name;
|
||||
node.outputs = subflow.out.length;
|
||||
node.inputs = subflow.in.length;
|
||||
} else {
|
||||
if (!node._def) {
|
||||
if (node.x && node.y) {
|
||||
node._def = {
|
||||
color:"#fee",
|
||||
defaults: {},
|
||||
label: "unknown: "+n.type,
|
||||
labelStyle: "node_label_italic",
|
||||
outputs: n.outputs||n.wires.length,
|
||||
set: registry.getNodeSet("node-red/unknown")
|
||||
}
|
||||
var orig = {};
|
||||
for (var p in n) {
|
||||
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
|
||||
orig[p] = n[p];
|
||||
}
|
||||
}
|
||||
node._orig = orig;
|
||||
node.name = n.type;
|
||||
node.type = "unknown";
|
||||
} else {
|
||||
node._def = {
|
||||
category:"config",
|
||||
set: registry.getNodeSet("node-red/unknown")
|
||||
};
|
||||
node.users = [];
|
||||
}
|
||||
if (node._def.category != "config") {
|
||||
node.inputs = n.inputs||node._def.inputs;
|
||||
node.outputs = n.outputs||node._def.outputs;
|
||||
for (var d2 in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d2)) {
|
||||
var orig = {};
|
||||
for (var p in n) {
|
||||
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
|
||||
orig[p] = n[p];
|
||||
}
|
||||
}
|
||||
node._orig = orig;
|
||||
node.name = n.type;
|
||||
node.type = "unknown";
|
||||
}
|
||||
if (node._def.category != "config") {
|
||||
node.inputs = n.inputs||node._def.inputs;
|
||||
node.outputs = n.outputs||node._def.outputs;
|
||||
for (var d2 in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d2)) {
|
||||
if (node._def.defaults[d2].type) {
|
||||
if (node_map[n[d2]]) {
|
||||
node[d2] = node_map[n[d2]].id;
|
||||
} else {
|
||||
node[d2] = n[d2];
|
||||
}
|
||||
} else {
|
||||
node[d2] = n[d2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addNode(node);
|
||||
RED.editor.validateNode(node);
|
||||
node_map[n.id] = node;
|
||||
if (node._def.category != "config") {
|
||||
new_nodes.push(node);
|
||||
}
|
||||
}
|
||||
addNode(node);
|
||||
RED.editor.validateNode(node);
|
||||
node_map[n.id] = node;
|
||||
if (node._def.category != "config") {
|
||||
new_nodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
n = new_nodes[i];
|
||||
}
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
n = new_nodes[i];
|
||||
if (n.wires) {
|
||||
for (var w1=0;w1<n.wires.length;w1++) {
|
||||
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
|
||||
for (var w2=0;w2<wires.length;w2++) {
|
||||
@@ -730,72 +875,110 @@ RED.nodes = (function() {
|
||||
}
|
||||
delete n.wires;
|
||||
}
|
||||
for (i=0;i<new_subflows.length;i++) {
|
||||
n = new_subflows[i];
|
||||
n.in.forEach(function(input) {
|
||||
input.wires.forEach(function(wire) {
|
||||
var link = {source:input, sourcePort:0, target:node_map[wire.id]};
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
});
|
||||
delete input.wires;
|
||||
}
|
||||
for (i=0;i<new_subflows.length;i++) {
|
||||
n = new_subflows[i];
|
||||
n.in.forEach(function(input) {
|
||||
input.wires.forEach(function(wire) {
|
||||
var link = {source:input, sourcePort:0, target:node_map[wire.id]};
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
});
|
||||
n.out.forEach(function(output) {
|
||||
output.wires.forEach(function(wire) {
|
||||
var link;
|
||||
if (wire.id == n.id) {
|
||||
link = {source:n.in[wire.port], sourcePort:wire.port,target:output};
|
||||
} else {
|
||||
link = {source:node_map[wire.id], sourcePort:wire.port,target:output};
|
||||
}
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
});
|
||||
delete output.wires;
|
||||
delete input.wires;
|
||||
});
|
||||
n.out.forEach(function(output) {
|
||||
output.wires.forEach(function(wire) {
|
||||
var link;
|
||||
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
|
||||
link = {source:n.in[wire.port], sourcePort:wire.port,target:output};
|
||||
} else {
|
||||
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:output};
|
||||
}
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
});
|
||||
}
|
||||
return [new_nodes,new_links,new_workspaces,new_subflows];
|
||||
} catch(error) {
|
||||
if (error.code != "NODE_RED") {
|
||||
console.log(error.stack);
|
||||
RED.notify("<strong>Error</strong>: "+error,"error");
|
||||
} else {
|
||||
RED.notify("<strong>Error</strong>: "+error.message,"error");
|
||||
}
|
||||
return null;
|
||||
delete output.wires;
|
||||
});
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
return [new_nodes,new_links,new_workspaces,new_subflows];
|
||||
}
|
||||
|
||||
// TODO: supports filter.z|type
|
||||
function filterNodes(filter) {
|
||||
var result = [];
|
||||
|
||||
for (var n=0;n<nodes.length;n++) {
|
||||
var node = nodes[n];
|
||||
if (filter.hasOwnProperty("z") && node.z !== filter.z) {
|
||||
continue;
|
||||
}
|
||||
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
||||
continue;
|
||||
}
|
||||
result.push(node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function filterLinks(filter) {
|
||||
var result = [];
|
||||
|
||||
for (var n=0;n<links.length;n++) {
|
||||
var link = links[n];
|
||||
if (filter.source) {
|
||||
if (filter.source.hasOwnProperty("id") && link.source.id !== filter.source.id) {
|
||||
continue;
|
||||
}
|
||||
if (filter.source.hasOwnProperty("z") && link.source.z !== filter.source.z) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (filter.target) {
|
||||
if (filter.target.hasOwnProperty("id") && link.target.id !== filter.target.id) {
|
||||
continue;
|
||||
}
|
||||
if (filter.target.hasOwnProperty("z") && link.target.z !== filter.target.z) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (filter.hasOwnProperty("sourcePort") && link.sourcePort !== filter.sourcePort) {
|
||||
continue;
|
||||
}
|
||||
result.push(link);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
registry:registry,
|
||||
setNodeList: registry.setNodeList,
|
||||
|
||||
|
||||
getNodeSet: registry.getNodeSet,
|
||||
addNodeSet: registry.addNodeSet,
|
||||
removeNodeSet: registry.removeNodeSet,
|
||||
enableNodeSet: registry.enableNodeSet,
|
||||
disableNodeSet: registry.disableNodeSet,
|
||||
|
||||
|
||||
registerType: registry.registerNodeType,
|
||||
getType: registry.getNodeType,
|
||||
convertNode: convertNode,
|
||||
|
||||
|
||||
add: addNode,
|
||||
remove: removeNode,
|
||||
|
||||
|
||||
addLink: addLink,
|
||||
removeLink: removeLink,
|
||||
|
||||
|
||||
addWorkspace: addWorkspace,
|
||||
removeWorkspace: removeWorkspace,
|
||||
workspace: getWorkspace,
|
||||
|
||||
|
||||
addSubflow: addSubflow,
|
||||
removeSubflow: removeSubflow,
|
||||
subflow: getSubflow,
|
||||
subflowContains: subflowContains,
|
||||
|
||||
|
||||
eachNode: function(cb) {
|
||||
for (var n=0;n<nodes.length;n++) {
|
||||
cb(nodes[n]);
|
||||
@@ -820,14 +1003,31 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
eachWorkspace: function(cb) {
|
||||
for (var id in workspaces) {
|
||||
if (workspaces.hasOwnProperty(id)) {
|
||||
cb(workspaces[id]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
node: getNode,
|
||||
|
||||
filterNodes: filterNodes,
|
||||
filterLinks: filterLinks,
|
||||
|
||||
import: importNodes,
|
||||
refreshValidation: refreshValidation,
|
||||
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
id: getID,
|
||||
nodes: nodes, // TODO: exposed for d3 vis
|
||||
links: links // TODO: exposed for d3 vis
|
||||
dirty: function(d) {
|
||||
if (d == null) {
|
||||
return dirty;
|
||||
} else {
|
||||
setDirty(d);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
@@ -16,9 +16,9 @@
|
||||
|
||||
|
||||
RED.settings = (function () {
|
||||
|
||||
|
||||
var loadedSettings = {};
|
||||
|
||||
|
||||
var hasLocalStorage = function () {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
@@ -68,13 +68,20 @@ RED.settings = (function () {
|
||||
};
|
||||
|
||||
var init = function (done) {
|
||||
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
|
||||
if (accessTokenMatch) {
|
||||
var accessToken = accessTokenMatch[1];
|
||||
RED.settings.set("auth-tokens",{access_token: accessToken});
|
||||
window.location.search = "";
|
||||
}
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(jqXHR,settings) {
|
||||
// Only attach auth header for requests to relative paths
|
||||
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
if (auth_tokens) {
|
||||
jqXHR.setRequestHeader("authorization","bearer "+auth_tokens.access_token);
|
||||
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,9 +89,8 @@ RED.settings = (function () {
|
||||
|
||||
load(done);
|
||||
}
|
||||
|
||||
|
||||
var load = function(done) {
|
||||
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
@@ -102,6 +108,9 @@ RED.settings = (function () {
|
||||
},
|
||||
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:",jqXHR.status,textStatus);
|
||||
@@ -110,13 +119,30 @@ RED.settings = (function () {
|
||||
});
|
||||
};
|
||||
|
||||
function theme(property,defaultValue) {
|
||||
if (!RED.settings.editorTheme) {
|
||||
return defaultValue;
|
||||
}
|
||||
var parts = property.split(".");
|
||||
var v = RED.settings.editorTheme;
|
||||
try {
|
||||
for (var i=0;i<parts.length;i++) {
|
||||
v = v[parts[i]];
|
||||
}
|
||||
return v;
|
||||
} catch(err) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
load: load,
|
||||
set: set,
|
||||
get: get,
|
||||
remove: remove
|
||||
remove: remove,
|
||||
|
||||
theme: theme
|
||||
}
|
||||
})
|
||||
();
|
185
editor/js/ui/clipboard.js
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.clipboard = (function() {
|
||||
|
||||
var dialog;
|
||||
var dialogContainer;
|
||||
var exportNodesDialog;
|
||||
var importNodesDialog;
|
||||
|
||||
function setupDialogs() {
|
||||
dialog = $('<div id="clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
id: "clipboard-dialog-ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
RED.view.importNodes($("#clipboard-import").val());
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-close",
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
|
||||
dialogContainer = dialog.children(".dialog-form");
|
||||
|
||||
exportNodesDialog = '<div class="form-row">'+
|
||||
'<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> '+RED._("clipboard.nodes")+'</label>'+
|
||||
'<textarea readonly style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
|
||||
'</div>'+
|
||||
'<div class="form-tips">'+
|
||||
RED._("clipboard.selectNodes")+
|
||||
'</div>';
|
||||
|
||||
importNodesDialog = '<div class="form-row">'+
|
||||
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+
|
||||
RED._("clipboard.pasteNodes")+
|
||||
'"></textarea>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function validateImport() {
|
||||
var importInput = $("#clipboard-import");
|
||||
var v = importInput.val();
|
||||
v = v.substring(v.indexOf('['),v.lastIndexOf(']')+1);
|
||||
try {
|
||||
JSON.parse(v);
|
||||
importInput.removeClass("input-error");
|
||||
importInput.val(v);
|
||||
$("#clipboard-dialog-ok").button("enable");
|
||||
} catch(err) {
|
||||
if (v !== "") {
|
||||
importInput.addClass("input-error");
|
||||
}
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
}
|
||||
}
|
||||
|
||||
function importNodes() {
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(importNodesDialog));
|
||||
$("#clipboard-dialog-ok").show();
|
||||
$("#clipboard-dialog-cancel").show();
|
||||
$("#clipboard-dialog-close").hide();
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
$("#clipboard-import").keyup(validateImport);
|
||||
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
|
||||
}
|
||||
|
||||
function exportNodes() {
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(exportNodesDialog));
|
||||
$("#clipboard-dialog-ok").hide();
|
||||
$("#clipboard-dialog-cancel").hide();
|
||||
$("#clipboard-dialog-close").show();
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var nns = RED.nodes.createExportableNodeSet(selection.nodes);
|
||||
$("#clipboard-export")
|
||||
.val(JSON.stringify(nns))
|
||||
.focus(function() {
|
||||
var textarea = $(this);
|
||||
textarea.select();
|
||||
textarea.mouseup(function() {
|
||||
textarea.unbind("mouseup");
|
||||
return false;
|
||||
})
|
||||
});
|
||||
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
|
||||
}
|
||||
}
|
||||
|
||||
function hideDropTarget() {
|
||||
$("#dropTarget").hide();
|
||||
RED.keyboard.remove(/* ESCAPE */ 27);
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
setupDialogs();
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-export",true);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",true);
|
||||
RED.menu.setDisabled("menu-item-export-library",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-export",false);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",false);
|
||||
RED.menu.setDisabled("menu-item-export-library",false);
|
||||
}
|
||||
});
|
||||
RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
|
||||
RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
|
||||
|
||||
$('#chart').on("dragenter",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
$("#dropTarget").css({display:'table'});
|
||||
RED.keyboard.add(/* ESCAPE */ 27,hideDropTarget);
|
||||
}
|
||||
});
|
||||
|
||||
$('#dropTarget').on("dragover",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
.on("dragleave",function(event) {
|
||||
hideDropTarget();
|
||||
})
|
||||
.on("drop",function(event) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
hideDropTarget();
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
RED.view.importNodes(data);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
},
|
||||
import: importNodes,
|
||||
export: exportNodes
|
||||
}
|
||||
})();
|
286
editor/js/ui/deploy.js
Normal file
@@ -0,0 +1,286 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.deploy = (function() {
|
||||
|
||||
var deploymentTypes = {
|
||||
"full":{img:"red/images/deploy-full-o.png"},
|
||||
"nodes":{img:"red/images/deploy-nodes-o.png"},
|
||||
"flows":{img:"red/images/deploy-flows-o.png"}
|
||||
}
|
||||
|
||||
var ignoreDeployWarnings = {
|
||||
unknown: false,
|
||||
unusedConfig: false,
|
||||
invalid: false
|
||||
}
|
||||
|
||||
var deploymentType = "full";
|
||||
|
||||
function changeDeploymentType(type) {
|
||||
deploymentType = type;
|
||||
$("#btn-deploy img").attr("src",deploymentTypes[type].img);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* options:
|
||||
* type: "default" - Button with drop-down options - no further customisation available
|
||||
* type: "simple" - Button without dropdown. Customisations:
|
||||
* label: the text to display - default: "Deploy"
|
||||
* icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.png"
|
||||
*/
|
||||
function init(options) {
|
||||
options = options || {};
|
||||
var type = options.type || "default";
|
||||
|
||||
if (type == "default") {
|
||||
$('<li><span class="deploy-button-group button-group">'+
|
||||
'<a id="btn-deploy" class="deploy-button disabled" href="#"><img id="btn-deploy-icon" src="red/images/deploy-full-o.png"> <span>'+RED._("deploy.deploy")+'</span></a>'+
|
||||
'<a id="btn-deploy-options" data-toggle="dropdown" class="deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
RED.menu.init({id:"btn-deploy-options",
|
||||
options: [
|
||||
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.png",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
|
||||
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.png",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
|
||||
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.png",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}}
|
||||
]
|
||||
});
|
||||
} else if (type == "simple") {
|
||||
var label = options.label || RED._("deploy.deploy");
|
||||
var icon = 'red/images/deploy-full-o.png';
|
||||
if (options.hasOwnProperty('icon')) {
|
||||
icon = options.icon;
|
||||
}
|
||||
|
||||
$('<li><span class="deploy-button-group button-group">'+
|
||||
'<a id="btn-deploy" class="deploy-button disabled" href="#">'+
|
||||
(icon?'<img id="btn-deploy-icon" src="'+icon+'"> ':'')+
|
||||
'<span>'+label+'</span></a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
}
|
||||
|
||||
$('#btn-deploy').click(function() { save(); });
|
||||
|
||||
$( "#node-dialog-confirm-deploy" ).dialog({
|
||||
title: "Confirm deploy",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 550,
|
||||
height: "auto",
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("deploy.confirm.button.confirm"),
|
||||
click: function() {
|
||||
|
||||
var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked");
|
||||
if (ignoreChecked) {
|
||||
ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true;
|
||||
}
|
||||
save(true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("deploy.confirm.button.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
create: function() {
|
||||
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
|
||||
.prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
|
||||
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
|
||||
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
|
||||
'<input type="hidden" id="node-dialog-confirm-deploy-type">'+
|
||||
'</div>');
|
||||
}
|
||||
});
|
||||
|
||||
RED.events.on('nodes:change',function(state) {
|
||||
if (state.dirty) {
|
||||
window.onbeforeunload = function() {
|
||||
return RED._("deploy.confirm.undeployedChanges");
|
||||
}
|
||||
$("#btn-deploy").removeClass("disabled");
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
$("#btn-deploy").addClass("disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getNodeInfo(node) {
|
||||
var tabLabel = "";
|
||||
if (node.z) {
|
||||
var tab = RED.nodes.workspace(node.z);
|
||||
if (!tab) {
|
||||
tab = RED.nodes.subflow(node.z);
|
||||
tabLabel = tab.name;
|
||||
} else {
|
||||
tabLabel = tab.label;
|
||||
}
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
return {tab:tabLabel,type:node.type,label:label};
|
||||
}
|
||||
function sortNodeInfo(A,B) {
|
||||
if (A.tab < B.tab) { return -1;}
|
||||
if (A.tab > B.tab) { return 1;}
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
if (A.name < B.name) { return -1;}
|
||||
if (A.name > B.name) { return 1;}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function save(force) {
|
||||
if (RED.nodes.dirty()) {
|
||||
//$("#debug-tab-clear").click(); // uncomment this to auto clear debug on deploy
|
||||
|
||||
if (!force) {
|
||||
var hasUnknown = false;
|
||||
var hasInvalid = false;
|
||||
var hasUnusedConfig = false;
|
||||
|
||||
var unknownNodes = [];
|
||||
var invalidNodes = [];
|
||||
|
||||
RED.nodes.eachNode(function(node) {
|
||||
hasInvalid = hasInvalid || !node.valid;
|
||||
if (!node.valid) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
if (unknownNodes.indexOf(node.name) == -1) {
|
||||
unknownNodes.push(node.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
hasUnknown = unknownNodes.length > 0;
|
||||
|
||||
var unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (node.users.length === 0) {
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
});
|
||||
|
||||
$( "#node-dialog-confirm-deploy-config" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unused" ).hide();
|
||||
|
||||
var showWarning = false;
|
||||
|
||||
if (hasUnknown && !ignoreDeployWarnings.unknown) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("unknown");
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).show();
|
||||
$( "#node-dialog-confirm-deploy-unknown-list" )
|
||||
.html("<li>"+unknownNodes.join("</li><li>")+"</li>");
|
||||
} else if (hasInvalid && !ignoreDeployWarnings.invalid) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("invalid");
|
||||
$( "#node-dialog-confirm-deploy-config" ).show();
|
||||
invalidNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-invalid-list" )
|
||||
.html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
|
||||
} else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
|
||||
// showWarning = true;
|
||||
// $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
// $( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
//
|
||||
// unusedConfigNodes.sort(sortNodeInfo);
|
||||
// $( "#node-dialog-confirm-deploy-unused-list" )
|
||||
// .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
}
|
||||
if (showWarning) {
|
||||
$( "#node-dialog-confirm-deploy-hide" ).prop("checked",false);
|
||||
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var nns = RED.nodes.createCompleteNodeSet();
|
||||
|
||||
$("#btn-deploy-icon").removeClass('fa-download');
|
||||
$("#btn-deploy-icon").addClass('spinner');
|
||||
RED.nodes.dirty(false);
|
||||
|
||||
$.ajax({
|
||||
url:"flows",
|
||||
type: "POST",
|
||||
data: JSON.stringify(nns),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
headers: {
|
||||
"Node-RED-Deployment-Type":deploymentType
|
||||
}
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
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(RED._("deploy.successfulDeploy"),"success");
|
||||
}
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
}
|
||||
if(node.credentials) {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function (confNode) {
|
||||
if (confNode.credentials) {
|
||||
delete confNode.credentials;
|
||||
}
|
||||
});
|
||||
// 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);
|
||||
if (xhr.responseText) {
|
||||
RED.notify(RED._("notification.error",{message:xhr.responseText}),"error");
|
||||
} else {
|
||||
RED.notify(RED._("notification.error",{message:RED._("deploy.errors.noResponse")}),"error");
|
||||
}
|
||||
}).always(function() {
|
||||
$("#btn-deploy-icon").removeClass('spinner');
|
||||
$("#btn-deploy-icon").addClass('fa-download');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
})();
|
1224
editor/js/ui/editor.js
Normal file
121
editor/js/ui/keyboard.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.keyboard = (function() {
|
||||
|
||||
var active = true;
|
||||
var handlers = {};
|
||||
|
||||
d3.select(window).on("keydown",function() {
|
||||
if (!active) { return; }
|
||||
var handler = handlers[d3.event.keyCode];
|
||||
if (handler && handler.ondown) {
|
||||
if (!handler.modifiers ||
|
||||
((!handler.modifiers.shift || d3.event.shiftKey) &&
|
||||
(!handler.modifiers.ctrl || d3.event.ctrlKey || d3.event.metaKey) &&
|
||||
(!handler.modifiers.alt || d3.event.altKey) )) {
|
||||
handler.ondown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
d3.select(window).on("keyup",function() {
|
||||
if (!active) { return; }
|
||||
var handler = handlers[d3.event.keyCode];
|
||||
if (handler && handler.onup) {
|
||||
if (!handler.modifiers ||
|
||||
((!handler.modifiers.shift || d3.event.shiftKey) &&
|
||||
(!handler.modifiers.ctrl || d3.event.ctrlKey || d3.event.metaKey) &&
|
||||
(!handler.modifiers.alt || d3.event.altKey) )) {
|
||||
handler.onup();
|
||||
}
|
||||
}
|
||||
});
|
||||
function addHandler(key,modifiers,ondown,onup) {
|
||||
var mod = modifiers;
|
||||
var cbdown = ondown;
|
||||
var cbup = onup;
|
||||
if (typeof modifiers == "function") {
|
||||
mod = {};
|
||||
cbdown = modifiers;
|
||||
cbup = ondown;
|
||||
}
|
||||
handlers[key] = {modifiers:mod, ondown:cbdown, onup:cbup};
|
||||
}
|
||||
function removeHandler(key) {
|
||||
delete handlers[key];
|
||||
}
|
||||
|
||||
|
||||
var dialog = null;
|
||||
|
||||
function showKeyboardHelp() {
|
||||
if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) {
|
||||
return;
|
||||
}
|
||||
if (!dialog) {
|
||||
dialog = $('<div id="keyboard-help-dialog" class="hide">'+
|
||||
'<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+
|
||||
'<table class="keyboard-shortcuts">'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">a</span></td><td>'+RED._("keyboard.selectAll")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Shift</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.selectAllConnected")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.addRemoveNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteSelected")+'</td></tr>'+
|
||||
'<tr><td> </td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">i</span></td><td>'+RED._("keyboard.importNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">e</span></td><td>'+RED._("keyboard.exportNode")+'</td></tr>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+
|
||||
'<table class="keyboard-shortcuts">'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Space</span></td><td>'+RED._("keyboard.toggleSidebar")+'</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteNode")+'</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">c</span></td><td>'+RED._("keyboard.copyNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">x</span></td><td>'+RED._("keyboard.cutNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">v</span></td><td>'+RED._("keyboard.pasteNode")+'</td></tr>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'</div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: "800",
|
||||
title:"Keyboard shortcuts",
|
||||
resizable: false,
|
||||
open: function() {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function() {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dialog.dialog("open");
|
||||
}
|
||||
|
||||
return {
|
||||
add: addHandler,
|
||||
remove: removeHandler,
|
||||
disable: function(){ active = false;},
|
||||
enable: function(){ active = true; },
|
||||
|
||||
showHelp: showKeyboardHelp
|
||||
}
|
||||
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.library = (function() {
|
||||
|
||||
|
||||
|
||||
|
||||
var exportToLibraryDialog;
|
||||
|
||||
function loadFlowLibrary() {
|
||||
$.getJSON("library/flows",function(data) {
|
||||
//console.log(data);
|
||||
@@ -25,7 +27,7 @@ RED.library = (function() {
|
||||
var li;
|
||||
var a;
|
||||
var ul = document.createElement("ul");
|
||||
ul.id = "btn-import-library-submenu";
|
||||
ul.id = "menu-item-import-library-submenu";
|
||||
ul.className = "dropdown-menu";
|
||||
if (data.d) {
|
||||
for (i in data.d) {
|
||||
@@ -63,22 +65,35 @@ RED.library = (function() {
|
||||
};
|
||||
var menu = buildMenu(data,"");
|
||||
//TODO: need an api in RED.menu for this
|
||||
$("#btn-import-library-submenu").replaceWith(menu);
|
||||
$("#menu-item-import-library-submenu").replaceWith(menu);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createUI(options) {
|
||||
var libraryData = {};
|
||||
var selectedLibraryItem = null;
|
||||
var libraryEditor = null;
|
||||
|
||||
|
||||
// Orion editor has set/getText
|
||||
// ACE editor has set/getValue
|
||||
// normalise to set/getValue
|
||||
if (options.editor.setText) {
|
||||
// Orion doesn't like having pos passed in, so proxy the call to drop it
|
||||
options.editor.setValue = function(text,pos) {
|
||||
options.editor.setText.call(options.editor,text);
|
||||
}
|
||||
}
|
||||
if (options.editor.getText) {
|
||||
options.editor.getValue = options.editor.getText;
|
||||
}
|
||||
|
||||
function buildFileListItem(item) {
|
||||
var li = document.createElement("li");
|
||||
li.onmouseover = function(e) { $(this).addClass("list-hover"); };
|
||||
li.onmouseout = function(e) { $(this).removeClass("list-hover"); };
|
||||
return li;
|
||||
}
|
||||
|
||||
|
||||
function buildFileList(root,data) {
|
||||
var ul = document.createElement("ul");
|
||||
var li;
|
||||
@@ -91,7 +106,7 @@ RED.library = (function() {
|
||||
var dirName = v;
|
||||
return function(e) {
|
||||
var bcli = $('<li class="active"><span class="divider">/</span> <a href="#">'+dirName+'</a></li>');
|
||||
$("a",bcli).click(function(e) {
|
||||
$("a",bcli).click(function(e) {
|
||||
$(this).parent().nextAll().remove();
|
||||
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
||||
@@ -119,7 +134,7 @@ RED.library = (function() {
|
||||
$(this).addClass("list-selected");
|
||||
$.get("library/"+options.url+root+item.fn, function(data) {
|
||||
selectedLibraryItem = item;
|
||||
libraryEditor.setText(data);
|
||||
libraryEditor.setValue(data,-1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -128,24 +143,24 @@ RED.library = (function() {
|
||||
}
|
||||
return ul;
|
||||
}
|
||||
|
||||
$('#node-input-name').addClass('input-append-left').css("width","65%").after(
|
||||
'<div class="btn-group" style="margin-left: 0px;">'+
|
||||
'<button id="node-input-'+options.type+'-lookup" class="btn input-append-right" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></button>'+
|
||||
|
||||
$('#node-input-name').css("width","60%").after(
|
||||
'<div class="btn-group" style="margin-left: 5px;">'+
|
||||
'<a id="node-input-'+options.type+'-lookup" class="editor-button" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></a>'+
|
||||
'<ul class="dropdown-menu pull-right" role="menu">'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">Open Library...</a></li>'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">Save to Library...</a></li>'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">'+RED._("library.openLibrary")+'</a></li>'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">'+RED._("library.saveToLibrary")+'</a></li>'+
|
||||
'</ul></div>'
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$('#node-input-'+options.type+'-menu-open-library').click(function(e) {
|
||||
$("#node-select-library").children().remove();
|
||||
var bc = $("#node-dialog-library-breadcrumbs");
|
||||
bc.children().first().nextAll().remove();
|
||||
libraryEditor.setText('');
|
||||
|
||||
libraryEditor.setValue('',-1);
|
||||
|
||||
$.getJSON("library/"+options.url,function(data) {
|
||||
$("#node-select-library").append(buildFileList("/",data));
|
||||
$("#node-dialog-library-breadcrumbs a").click(function(e) {
|
||||
@@ -155,10 +170,10 @@ RED.library = (function() {
|
||||
});
|
||||
$( "#node-dialog-library-lookup" ).dialog( "open" );
|
||||
});
|
||||
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
$('#node-input-'+options.type+'-menu-save-library').click(function(e) {
|
||||
//var found = false;
|
||||
var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
@@ -204,38 +219,42 @@ RED.library = (function() {
|
||||
$( "#node-dialog-library-save" ).dialog( "open" );
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
require(["orion/editor/edit"], function(edit) {
|
||||
libraryEditor = edit({
|
||||
parent:document.getElementById('node-select-library-text'),
|
||||
lang:"js",
|
||||
readonly: true
|
||||
});
|
||||
|
||||
libraryEditor = ace.edit('node-select-library-text');
|
||||
libraryEditor.setTheme("ace/theme/tomorrow");
|
||||
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;
|
||||
|
||||
$( "#node-dialog-library-lookup" ).dialog({
|
||||
title: options.type+" library",
|
||||
title: RED._("library.typeLibrary", {type:options.type}),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 800,
|
||||
height: 450,
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
if (selectedLibraryItem) {
|
||||
for (var i=0;i<options.fields.length;i++) {
|
||||
var field = options.fields[i];
|
||||
$("#node-input-"+field).val(selectedLibraryItem[field]);
|
||||
}
|
||||
options.editor.setText(libraryEditor.getText());
|
||||
options.editor.setValue(libraryEditor.getValue(),-1);
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
@@ -253,16 +272,16 @@ RED.library = (function() {
|
||||
$(".form-row:last-child",form).children().height(form.height()-60);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function saveToLibrary(overwrite) {
|
||||
var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
if (name === "") {
|
||||
name = "Unnamed "+options.type;
|
||||
name = RED._("library.unnamedType",{type:options.type});
|
||||
}
|
||||
var filename = $("#node-dialog-library-save-filename").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
var pathname = $("#node-dialog-library-save-folder").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
if (filename === "" || !/.+\.js$/.test(filename)) {
|
||||
RED.notify("Invalid filename","warning");
|
||||
RED.notify(RED._("library.invalidFilename"),"warning");
|
||||
return;
|
||||
}
|
||||
var fullpath = pathname+(pathname===""?"":"/")+filename;
|
||||
@@ -287,44 +306,50 @@ RED.library = (function() {
|
||||
// }
|
||||
//}
|
||||
//if (exists) {
|
||||
// $("#node-dialog-library-save-type").html(options.type);
|
||||
// $("#node-dialog-library-save-name").html(fullpath);
|
||||
// $("#node-dialog-library-save-content").html(RED._("library.dialogSaveOverwrite",{libraryType:options.type,libraryName:fullpath}));
|
||||
// $("#node-dialog-library-save-confirm").dialog( "open" );
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
var queryArgs = [];
|
||||
var data = {};
|
||||
for (var i=0;i<options.fields.length;i++) {
|
||||
var field = options.fields[i];
|
||||
if (field == "name") {
|
||||
queryArgs.push("name="+encodeURIComponent(name));
|
||||
data.name = name;
|
||||
} else {
|
||||
queryArgs.push(encodeURIComponent(field)+"="+encodeURIComponent($("#node-input-"+field).val()));
|
||||
data[field] = $("#node-input-"+field).val();
|
||||
}
|
||||
}
|
||||
var queryString = queryArgs.join("&");
|
||||
|
||||
var text = options.editor.getText();
|
||||
$.post("library/"+options.url+'/'+fullpath+"?"+queryString,text,function() {
|
||||
RED.notify("Saved "+options.type,"success");
|
||||
|
||||
data.text = options.editor.getValue();
|
||||
$.ajax({
|
||||
url:"library/"+options.url+'/'+fullpath,
|
||||
type: "POST",
|
||||
data: JSON.stringify(data),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.notify(RED._("library.savedType", {type:options.type}),"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
||||
});
|
||||
}
|
||||
$( "#node-dialog-library-save-confirm" ).dialog({
|
||||
title: "Save to library",
|
||||
title: RED._("library.saveToLibrary"),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
saveToLibrary(true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
@@ -332,21 +357,21 @@ RED.library = (function() {
|
||||
]
|
||||
});
|
||||
$( "#node-dialog-library-save" ).dialog({
|
||||
title: "Save to library",
|
||||
title: RED._("library.saveToLibrary"),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
saveToLibrary(false);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
@@ -355,14 +380,90 @@ RED.library = (function() {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
function exportFlow() {
|
||||
//TODO: don't rely on the main dialog
|
||||
var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes);
|
||||
$("#node-input-library-filename").attr('nodes',JSON.stringify(nns));
|
||||
exportToLibraryDialog.dialog( "open" );
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
loadFlowLibrary();
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-export",true);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",true);
|
||||
RED.menu.setDisabled("menu-item-export-library",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-export",false);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",false);
|
||||
RED.menu.setDisabled("menu-item-export-library",false);
|
||||
}
|
||||
});
|
||||
|
||||
if (RED.settings.theme("menu.menu-item-import-library") !== false) {
|
||||
loadFlowLibrary();
|
||||
}
|
||||
|
||||
exportToLibraryDialog = $('<div id="library-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
resizable: false,
|
||||
title: RED._("library.exportToLibrary"),
|
||||
buttons: [
|
||||
{
|
||||
id: "library-dialog-ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
//TODO: move this to RED.library
|
||||
var flowName = $("#node-input-library-filename").val();
|
||||
if (!/^\s*$/.test(flowName)) {
|
||||
$.ajax({
|
||||
url:'library/flows/'+flowName,
|
||||
type: "POST",
|
||||
data: $("#node-input-library-filename").attr('nodes'),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function() {
|
||||
RED.library.loadFlowLibrary();
|
||||
RED.notify(RED._("library.savedNodes"),"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
||||
});
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "library-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
exportToLibraryDialog.children(".dialog-form").append($(
|
||||
'<div class="form-row">'+
|
||||
'<label for="node-input-library-filename" data-i18n="[append]editor:library.filename"><i class="fa fa-file"></i> </label>'+
|
||||
'<input type="text" id="node-input-library-filename" data-i18n="[placeholder]editor:library.fullFilenamePlaceholder">'+
|
||||
'<input type="text" style="display: none;" />'+ // Second hidden input to prevent submit on Enter
|
||||
'</div>'
|
||||
));
|
||||
},
|
||||
create: createUI,
|
||||
loadFlowLibrary: loadFlowLibrary
|
||||
loadFlowLibrary: loadFlowLibrary,
|
||||
|
||||
export: exportFlow
|
||||
}
|
||||
})();
|
||||
|
||||
|
@@ -23,7 +23,14 @@ RED.menu = (function() {
|
||||
function createMenuItem(opt) {
|
||||
var item;
|
||||
|
||||
function setState() {
|
||||
if (opt !== null && opt.id) {
|
||||
var themeSetting = RED.settings.theme("menu."+opt.id);
|
||||
if (themeSetting === false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setInitialState() {
|
||||
var savedStateActive = isSavedStateActive(opt.id);
|
||||
if (savedStateActive) {
|
||||
link.addClass("active");
|
||||
@@ -45,12 +52,16 @@ RED.menu = (function() {
|
||||
item = $('<li class="divider"></li>');
|
||||
} else {
|
||||
item = $('<li></li>');
|
||||
|
||||
|
||||
if (opt.group) {
|
||||
item.addClass("menu-group-"+opt.group);
|
||||
|
||||
}
|
||||
var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
|
||||
if (opt.toggle) {
|
||||
linkContent += '<i class="fa fa-square pull-left"></i>';
|
||||
linkContent += '<i class="fa fa-check-square pull-left"></i>';
|
||||
|
||||
|
||||
}
|
||||
if (opt.icon !== undefined) {
|
||||
if (/\.png/.test(opt.icon)) {
|
||||
@@ -59,16 +70,16 @@ RED.menu = (function() {
|
||||
linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (opt.sublabel) {
|
||||
linkContent += '<span class="menu-label-container"><span class="menu-label">'+opt.label+'</span>'+
|
||||
'<span class="menu-sublabel">'+opt.sublabel+'</span></span>'
|
||||
} else {
|
||||
linkContent += '<span class="menu-label">'+opt.label+'</span>'
|
||||
}
|
||||
|
||||
|
||||
linkContent += '</a>';
|
||||
|
||||
|
||||
var link = $(linkContent).appendTo(item);
|
||||
|
||||
menuItems[opt.id] = opt;
|
||||
@@ -99,7 +110,7 @@ RED.menu = (function() {
|
||||
opt.onselect.call(opt);
|
||||
}
|
||||
});
|
||||
setState();
|
||||
setInitialState();
|
||||
} else if (opt.href) {
|
||||
link.attr("target","_blank").attr("href",opt.href);
|
||||
} else if (!opt.options) {
|
||||
@@ -113,23 +124,15 @@ RED.menu = (function() {
|
||||
var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item);
|
||||
|
||||
for (var i=0;i<opt.options.length;i++) {
|
||||
createMenuItem(opt.options[i]).appendTo(submenu);
|
||||
var li = createMenuItem(opt.options[i]);
|
||||
if (li) {
|
||||
li.appendTo(submenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt.disabled) {
|
||||
item.addClass("disabled");
|
||||
}
|
||||
if (opt.tip) {
|
||||
item.popover({
|
||||
placement:"left",
|
||||
trigger: "hover",
|
||||
delay: { show: 350, hide: 20 },
|
||||
html: true,
|
||||
container:'body',
|
||||
content: opt.tip
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -144,13 +147,20 @@ RED.menu = (function() {
|
||||
// $("#"+options.id+"-submenu").show();
|
||||
// event.preventDefault();
|
||||
//});
|
||||
|
||||
|
||||
|
||||
|
||||
var topMenu = $("<ul/>",{id:options.id+"-submenu", class:"dropdown-menu pull-right"}).insertAfter(button);
|
||||
|
||||
var lastAddedSeparator = false;
|
||||
for (var i=0;i<options.options.length;i++) {
|
||||
var opt = options.options[i];
|
||||
createMenuItem(opt).appendTo(topMenu);
|
||||
if (opt !== null || !lastAddedSeparator) {
|
||||
var li = createMenuItem(opt);
|
||||
if (li) {
|
||||
li.appendTo(topMenu);
|
||||
lastAddedSeparator = (opt === null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +186,7 @@ RED.menu = (function() {
|
||||
} else {
|
||||
$("#"+id).removeClass("active");
|
||||
}
|
||||
if (opt.onselect) {
|
||||
if (opt && opt.onselect) {
|
||||
opt.onselect.call(opt,state);
|
||||
}
|
||||
setSavedState(id, state);
|
||||
@@ -191,24 +201,47 @@ RED.menu = (function() {
|
||||
}
|
||||
|
||||
function addItem(id,opt) {
|
||||
createMenuItem(opt).appendTo("#"+id+"-submenu");
|
||||
var item = createMenuItem(opt);
|
||||
if (opt.group) {
|
||||
var groupItems = $("#"+id+"-submenu").children(".menu-group-"+opt.group);
|
||||
if (groupItems.length === 0) {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
} else {
|
||||
for (var i=0;i<groupItems.length;i++) {
|
||||
var groupItem = groupItems[i];
|
||||
var label = $(groupItem).find(".menu-label").html();
|
||||
if (opt.label < label) {
|
||||
$(groupItem).before(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i === groupItems.length) {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
}
|
||||
}
|
||||
function removeItem(id) {
|
||||
$("#"+id).parent().remove();
|
||||
}
|
||||
|
||||
function setAction(id,action) {
|
||||
menuItems[id].onselect = action;
|
||||
$("#"+id).click(function() {
|
||||
if ($(this).parent().hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
if (menuItems[id].toggle) {
|
||||
setSelected(id,!isSelected(id));
|
||||
} else {
|
||||
menuItems[id].onselect.call(menuItems[id]);
|
||||
}
|
||||
});
|
||||
var opt = menuItems[id];
|
||||
if (opt) {
|
||||
opt.onselect = action;
|
||||
$("#"+id).click(function() {
|
||||
if ($(this).parent().hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
if (menuItems[id].toggle) {
|
||||
setSelected(id,!isSelected(id));
|
||||
} else {
|
||||
menuItems[id].onselect.call(menuItems[id]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,10 @@ RED.notify = (function() {
|
||||
}
|
||||
var n = document.createElement("div");
|
||||
n.id="red-notification-"+c;
|
||||
n.className = "alert";
|
||||
n.className = "notification";
|
||||
n.fixed = fixed;
|
||||
if (type) {
|
||||
n.className = "alert alert-"+type;
|
||||
n.className = "notification notification-"+type;
|
||||
}
|
||||
n.style.display = "none";
|
||||
n.innerHTML = msg;
|
||||
@@ -44,11 +44,18 @@ RED.notify = (function() {
|
||||
return function() {
|
||||
currentNotifications.splice(currentNotifications.indexOf(nn),1);
|
||||
$(nn).slideUp(300, function() {
|
||||
nn.parentNode.removeChild(nn);
|
||||
nn.parentNode.removeChild(nn);
|
||||
});
|
||||
};
|
||||
})();
|
||||
if (!fixed) {
|
||||
$(n).click((function() {
|
||||
var nn = n;
|
||||
return function() {
|
||||
nn.close();
|
||||
window.clearTimeout(nn.timeoutid);
|
||||
};
|
||||
})());
|
||||
n.timeoutid = window.setTimeout(n.close,timeout||3000);
|
||||
}
|
||||
currentNotifications.push(n);
|
||||
@@ -56,4 +63,3 @@ RED.notify = (function() {
|
||||
return n;
|
||||
}
|
||||
})();
|
||||
|
451
editor/js/ui/palette.js
Normal file
@@ -0,0 +1,451 @@
|
||||
/**
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.palette = (function() {
|
||||
|
||||
var exclusion = ['config','unknown','deprecated'];
|
||||
var core = ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'];
|
||||
|
||||
var categoryContainers = {};
|
||||
|
||||
function createCategoryContainer(category, label){
|
||||
label = label || category.replace("_", " ");
|
||||
var catDiv = $('<div id="palette-container-'+category+'" class="palette-category palette-close hide">'+
|
||||
'<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-angle-down"></i><span>'+label+'</span></div>'+
|
||||
'<div class="palette-content" id="palette-base-category-'+category+'">'+
|
||||
'<div id="palette-'+category+'-input"></div>'+
|
||||
'<div id="palette-'+category+'-output"></div>'+
|
||||
'<div id="palette-'+category+'-function"></div>'+
|
||||
'</div>'+
|
||||
'</div>').appendTo("#palette-container");
|
||||
|
||||
categoryContainers[category] = {
|
||||
container: catDiv,
|
||||
close: function() {
|
||||
catDiv.removeClass("palette-open");
|
||||
catDiv.addClass("palette-closed");
|
||||
$("#palette-base-category-"+category).slideUp();
|
||||
$("#palette-header-"+category+" i").removeClass("expanded");
|
||||
},
|
||||
open: function() {
|
||||
catDiv.addClass("palette-open");
|
||||
catDiv.removeClass("palette-closed");
|
||||
$("#palette-base-category-"+category).slideDown();
|
||||
$("#palette-header-"+category+" i").addClass("expanded");
|
||||
},
|
||||
toggle: function() {
|
||||
if (catDiv.hasClass("palette-open")) {
|
||||
categoryContainers[category].close();
|
||||
} else {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$("#palette-header-"+category).on('click', function(e) {
|
||||
categoryContainers[category].toggle();
|
||||
});
|
||||
}
|
||||
|
||||
function setLabel(type, el,label, info) {
|
||||
var nodeWidth = 82;
|
||||
var nodeHeight = 25;
|
||||
var lineHeight = 20;
|
||||
var portHeight = 10;
|
||||
|
||||
var words = label.split(" ");
|
||||
|
||||
var displayLines = [];
|
||||
|
||||
var currentLine = words[0];
|
||||
var currentLineWidth = RED.view.calculateTextWidth(currentLine, "palette_label", 0);
|
||||
|
||||
for (var i=1;i<words.length;i++) {
|
||||
var newWidth = RED.view.calculateTextWidth(currentLine+" "+words[i], "palette_label", 0);
|
||||
if (newWidth < nodeWidth) {
|
||||
currentLine += " "+words[i];
|
||||
currentLineWidth = newWidth;
|
||||
} else {
|
||||
displayLines.push(currentLine);
|
||||
currentLine = words[i];
|
||||
currentLineWidth = RED.view.calculateTextWidth(currentLine, "palette_label", 0);
|
||||
}
|
||||
}
|
||||
displayLines.push(currentLine);
|
||||
|
||||
var lines = displayLines.join("<br/>");
|
||||
var multiLineNodeHeight = 8+(lineHeight*displayLines.length);
|
||||
el.css({height:multiLineNodeHeight+"px"});
|
||||
|
||||
var labelElement = el.find(".palette_label");
|
||||
labelElement.html(lines);
|
||||
|
||||
el.find(".palette_port").css({top:(multiLineNodeHeight/2-5)+"px"});
|
||||
|
||||
var popOverContent;
|
||||
try {
|
||||
var l = "<p><b>"+label+"</b></p>";
|
||||
if (label != type) {
|
||||
l = "<p><b>"+label+"</b><br/><i>"+type+"</i></p>";
|
||||
}
|
||||
popOverContent = $(l+(info?info:$("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim())
|
||||
.filter(function(n) {
|
||||
return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0)
|
||||
}).slice(0,2);
|
||||
} catch(err) {
|
||||
// Malformed HTML may cause errors. TODO: need to understand what can break
|
||||
// NON-NLS: internal debug
|
||||
console.log("Error generating pop-over label for ",type);
|
||||
console.log(err.toString());
|
||||
popOverContent = "<p><b>"+label+"</b></p><p>"+RED._("palette.noInfo")+"</p>";
|
||||
}
|
||||
|
||||
el.data('popover').setContent(popOverContent);
|
||||
}
|
||||
|
||||
function escapeNodeType(nt) {
|
||||
return nt.replace(" ","_").replace(".","_").replace(":","_");
|
||||
}
|
||||
|
||||
function addNodeType(nt,def) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
if ($("#palette_node_"+nodeTypeId).length) {
|
||||
return;
|
||||
}
|
||||
if (exclusion.indexOf(def.category)===-1) {
|
||||
|
||||
var category = def.category.replace(" ","_");
|
||||
var rootCategory = category.split("-")[0];
|
||||
|
||||
var d = document.createElement("div");
|
||||
d.id = "palette_node_"+nodeTypeId;
|
||||
d.type = nt;
|
||||
|
||||
var label;
|
||||
|
||||
if (typeof def.paletteLabel === "undefined") {
|
||||
label = /^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1];
|
||||
} else {
|
||||
label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||"";
|
||||
}
|
||||
|
||||
|
||||
$('<div/>',{class:"palette_label"+(def.align=="right"?" palette_label_right":"")}).appendTo(d);
|
||||
|
||||
d.className="palette_node";
|
||||
|
||||
|
||||
if (def.icon) {
|
||||
var icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon);
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
}
|
||||
|
||||
d.style.backgroundColor = def.color;
|
||||
|
||||
if (def.outputs > 0) {
|
||||
var portOut = document.createElement("div");
|
||||
portOut.className = "palette_port palette_port_output";
|
||||
d.appendChild(portOut);
|
||||
}
|
||||
|
||||
if (def.inputs > 0) {
|
||||
var portIn = document.createElement("div");
|
||||
portIn.className = "palette_port palette_port_input";
|
||||
d.appendChild(portIn);
|
||||
}
|
||||
|
||||
if ($("#palette-base-category-"+rootCategory).length === 0) {
|
||||
if(core.indexOf(rootCategory) !== -1){
|
||||
createCategoryContainer(rootCategory, RED._("node-red:palette.label."+rootCategory, {defaultValue:rootCategory}));
|
||||
} else {
|
||||
var ns = def.set.id;
|
||||
createCategoryContainer(rootCategory, RED._(ns+":palette.label."+rootCategory, {defaultValue:rootCategory}));
|
||||
}
|
||||
}
|
||||
$("#palette-container-"+rootCategory).show();
|
||||
|
||||
if ($("#palette-"+category).length === 0) {
|
||||
$("#palette-base-category-"+rootCategory).append('<div id="palette-'+category+'"></div>');
|
||||
}
|
||||
|
||||
$("#palette-"+category).append(d);
|
||||
d.onmousedown = function(e) { e.preventDefault(); };
|
||||
|
||||
RED.popover.create({
|
||||
target:$(d),
|
||||
content: "hi",
|
||||
delay: { show: 750, hide: 50 }
|
||||
});
|
||||
|
||||
// $(d).popover({
|
||||
// title:d.type,
|
||||
// placement:"right",
|
||||
// trigger: "hover",
|
||||
// delay: { show: 750, hide: 50 },
|
||||
// html: true,
|
||||
// container:'body'
|
||||
// });
|
||||
$(d).click(function() {
|
||||
RED.view.focus();
|
||||
var helpText;
|
||||
if (nt.indexOf("subflow:") === 0) {
|
||||
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"");
|
||||
} else {
|
||||
helpText = $("script[data-help-name|='"+d.type+"']").html()||"";
|
||||
}
|
||||
var help = '<div class="node-help">'+helpText+"</div>";
|
||||
RED.sidebar.info.set(help);
|
||||
});
|
||||
var chart = $("#chart");
|
||||
var chartOffset = chart.offset();
|
||||
var chartSVG = $("#chart>svg").get(0);
|
||||
var activeSpliceLink;
|
||||
var mouseX;
|
||||
var mouseY;
|
||||
var spliceTimer;
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
appendTo: 'body',
|
||||
revert: true,
|
||||
revertDuration: 50,
|
||||
start: function() {RED.view.focus();},
|
||||
stop: function() { d3.select('.link_splice').classed('link_splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}},
|
||||
drag: function(e,ui) {
|
||||
// TODO: this is the margin-left of palette node. Hard coding
|
||||
// it here makes me sad
|
||||
ui.position.left += 17.5;
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
mouseX = e.clientX - chartOffset.left+chart.scrollLeft();
|
||||
mouseY = e.clientY-chartOffset.top +chart.scrollTop();
|
||||
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
if (chartSVG.getIntersectionList) {
|
||||
var svgRect = chartSVG.createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
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);
|
||||
}
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (d3.select(nodes[i]).classed('link_background')) {
|
||||
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));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('link_splice',false);
|
||||
}
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('link_splice',true)
|
||||
} else {
|
||||
d3.select('.link_splice').classed('link_splice',false);
|
||||
}
|
||||
if (activeSpliceLink !== bestLink) {
|
||||
if (bestLink) {
|
||||
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
|
||||
} else {
|
||||
$(ui.helper).removeData('splice');
|
||||
}
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},200);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var nodeInfo = null;
|
||||
if (def.category == "subflows") {
|
||||
$(d).dblclick(function(e) {
|
||||
RED.workspaces.show(nt.substring(8));
|
||||
e.preventDefault();
|
||||
});
|
||||
nodeInfo = marked(def.info||"");
|
||||
}
|
||||
setLabel(nt,$(d),label,nodeInfo);
|
||||
|
||||
var categoryNode = $("#palette-container-"+category);
|
||||
if (categoryNode.find(".palette_node").length === 1) {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function removeNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
var paletteNode = $("#palette_node_"+nodeTypeId);
|
||||
var categoryNode = paletteNode.closest(".palette-category");
|
||||
paletteNode.remove();
|
||||
if (categoryNode.find(".palette_node").length === 0) {
|
||||
if (categoryNode.find("i").hasClass("expanded")) {
|
||||
categoryNode.find(".palette-content").slideToggle();
|
||||
categoryNode.find("i").toggleClass("expanded");
|
||||
}
|
||||
}
|
||||
}
|
||||
function hideNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
$("#palette_node_"+nodeTypeId).hide();
|
||||
}
|
||||
|
||||
function showNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
$("#palette_node_"+nodeTypeId).show();
|
||||
}
|
||||
|
||||
function refreshNodeTypes() {
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var paletteNode = $("#palette_node_subflow_"+sf.id.replace(".","_"));
|
||||
var portInput = paletteNode.find(".palette_port_input");
|
||||
var portOutput = paletteNode.find(".palette_port_output");
|
||||
|
||||
if (portInput.length === 0 && sf.in.length > 0) {
|
||||
var portIn = document.createElement("div");
|
||||
portIn.className = "palette_port palette_port_input";
|
||||
paletteNode.append(portIn);
|
||||
} else if (portInput.length !== 0 && sf.in.length === 0) {
|
||||
portInput.remove();
|
||||
}
|
||||
|
||||
if (portOutput.length === 0 && sf.out.length > 0) {
|
||||
var portOut = document.createElement("div");
|
||||
portOut.className = "palette_port palette_port_output";
|
||||
paletteNode.append(portOut);
|
||||
} else if (portOutput.length !== 0 && sf.out.length === 0) {
|
||||
portOutput.remove();
|
||||
}
|
||||
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||""));
|
||||
});
|
||||
}
|
||||
|
||||
function filterChange() {
|
||||
var val = $("#palette-search-input").val();
|
||||
if (val === "") {
|
||||
$("#palette-search-clear").hide();
|
||||
} else {
|
||||
$("#palette-search-clear").show();
|
||||
}
|
||||
|
||||
var re = new RegExp(val,'i');
|
||||
$("#palette-container .palette_node").each(function(i,el) {
|
||||
var currentLabel = $(el).find(".palette_label").text();
|
||||
if (val === "" || re.test(el.id) || re.test(currentLabel)) {
|
||||
$(this).show();
|
||||
} else {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
for (var category in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(category)) {
|
||||
if (categoryContainers[category].container
|
||||
.find(".palette_node")
|
||||
.filter(function() { return $(this).css('display') !== 'none'}).length === 0) {
|
||||
categoryContainers[category].close();
|
||||
} else {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
$(".palette-spinner").show();
|
||||
if (RED.settings.paletteCategories) {
|
||||
RED.settings.paletteCategories.forEach(function(category){
|
||||
createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category}));
|
||||
});
|
||||
} else {
|
||||
core.forEach(function(category){
|
||||
createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category}));
|
||||
});
|
||||
}
|
||||
|
||||
$("#palette-search-input").focus(function(e) {
|
||||
RED.keyboard.disable();
|
||||
});
|
||||
$("#palette-search-input").blur(function(e) {
|
||||
RED.keyboard.enable();
|
||||
});
|
||||
|
||||
$("#palette-search-clear").on("click",function(e) {
|
||||
e.preventDefault();
|
||||
$("#palette-search-input").val("");
|
||||
filterChange();
|
||||
$("#palette-search-input").focus();
|
||||
});
|
||||
|
||||
$("#palette-search-input").val("");
|
||||
$("#palette-search-input").on("keyup",function() {
|
||||
filterChange();
|
||||
});
|
||||
|
||||
$("#palette-search-input").on("focus",function() {
|
||||
$("body").one("mousedown",function() {
|
||||
$("#palette-search-input").blur();
|
||||
});
|
||||
});
|
||||
|
||||
$("#palette-collapse-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(cat)) {
|
||||
categoryContainers[cat].close();
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#palette-expand-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(cat)) {
|
||||
categoryContainers[cat].open();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add:addNodeType,
|
||||
remove:removeNodeType,
|
||||
hide:hideNodeType,
|
||||
show:showNodeType,
|
||||
refresh:refreshNodeTypes
|
||||
};
|
||||
})();
|
79
editor/js/ui/popover.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.popover = (function() {
|
||||
|
||||
|
||||
function createPopover(options) {
|
||||
var target = options.target;
|
||||
|
||||
var content = options.content;
|
||||
var delay = options.delay;
|
||||
var timer = null;
|
||||
var active;
|
||||
var div;
|
||||
|
||||
var openPopup = function() {
|
||||
if (active) {
|
||||
div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body");
|
||||
var targetPos = target.offset();
|
||||
var targetWidth = target.width();
|
||||
var targetHeight = target.height();
|
||||
|
||||
var divHeight = div.height();
|
||||
div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:targetPos.left+targetWidth+17});
|
||||
|
||||
div.fadeIn("fast");
|
||||
}
|
||||
}
|
||||
var closePopup = function() {
|
||||
if (!active) {
|
||||
if (div) {
|
||||
div.fadeOut("fast",function() {
|
||||
$(this).remove();
|
||||
});
|
||||
div = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.on('mouseenter',function(e) {
|
||||
clearTimeout(timer);
|
||||
active = true;
|
||||
timer = setTimeout(openPopup,delay.show);
|
||||
});
|
||||
target.on('mouseleave', function(e) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
active = false;
|
||||
setTimeout(closePopup,delay.hide);
|
||||
});
|
||||
var res = {
|
||||
setContent: function(_content) {
|
||||
content = _content;
|
||||
}
|
||||
}
|
||||
target.data('popover',res);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
create: createPopover
|
||||
}
|
||||
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,25 +20,78 @@ RED.sidebar = (function() {
|
||||
id:"sidebar-tabs",
|
||||
onchange:function(tab) {
|
||||
$("#sidebar-content").children().hide();
|
||||
$("#"+tab.id).show();
|
||||
$("#sidebar-footer").children().hide();
|
||||
if (tab.onchange) {
|
||||
tab.onchange.call(tab);
|
||||
}
|
||||
$(tab.content).show();
|
||||
if (tab.toolbar) {
|
||||
$(tab.toolbar).show();
|
||||
}
|
||||
},
|
||||
onremove: function(tab) {
|
||||
$("#"+tab.id).remove();
|
||||
}
|
||||
$(tab.content).hide();
|
||||
if (tab.onremove) {
|
||||
tab.onremove.call(tab);
|
||||
}
|
||||
},
|
||||
minimumActiveTabWidth: 110
|
||||
});
|
||||
|
||||
function addTab(title,content,closeable) {
|
||||
$("#sidebar-content").append(content);
|
||||
$(content).hide();
|
||||
sidebar_tabs.addTab({id:"tab-"+title,label:title,closeable:closeable});
|
||||
//content.style.position = "absolute";
|
||||
//$('#sidebar').tabs("refresh");
|
||||
|
||||
var knownTabs = {
|
||||
|
||||
};
|
||||
|
||||
function addTab(title,content,closeable,visible) {
|
||||
var options;
|
||||
if (typeof title === "string") {
|
||||
// TODO: legacy support in case anyone uses this...
|
||||
options = {
|
||||
id: content.id,
|
||||
label: title,
|
||||
name: title,
|
||||
content: content,
|
||||
closeable: closeable,
|
||||
visible: visible
|
||||
}
|
||||
} else if (typeof title === "object") {
|
||||
options = title;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$("#sidebar-content").append(options.content);
|
||||
$(options.content).hide();
|
||||
if (options.toolbar) {
|
||||
$("#sidebar-footer").append(options.toolbar);
|
||||
$(options.toolbar).hide();
|
||||
}
|
||||
$(options.content).hide();
|
||||
var id = options.id;
|
||||
|
||||
RED.menu.addItem("menu-item-view-menu",{
|
||||
id:"menu-item-view-menu-"+options.id,
|
||||
label:options.name,
|
||||
onselect:function() {
|
||||
showSidebar(options.id);
|
||||
},
|
||||
group: "sidebar-tabs"
|
||||
});
|
||||
|
||||
knownTabs[options.id] = options;
|
||||
|
||||
if (options.visible !== false) {
|
||||
sidebar_tabs.addTab(knownTabs[options.id]);
|
||||
}
|
||||
}
|
||||
|
||||
function removeTab(title) {
|
||||
sidebar_tabs.removeTab("tab-"+title);
|
||||
function removeTab(id) {
|
||||
sidebar_tabs.removeTab(id);
|
||||
$(knownTabs[id].content).remove();
|
||||
delete knownTabs[id];
|
||||
RED.menu.removeItem("menu-item-view-menu-"+id);
|
||||
}
|
||||
|
||||
|
||||
var sidebarSeparator = {};
|
||||
$("#sidebar-separator").draggable({
|
||||
axis: "x",
|
||||
@@ -51,27 +104,24 @@ RED.sidebar = (function() {
|
||||
sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2;
|
||||
|
||||
|
||||
if (!RED.menu.isSelected("btn-sidebar")) {
|
||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
||||
sidebarSeparator.opening = true;
|
||||
var newChartRight = 15;
|
||||
var newChartRight = 7;
|
||||
$("#sidebar").addClass("closing");
|
||||
$("#workspace").css("right",newChartRight);
|
||||
$("#chart-zoom-controls").css("right",newChartRight+20);
|
||||
$("#sidebar").width(0);
|
||||
RED.menu.setSelected("btn-sidebar",true);
|
||||
RED.view.resize();
|
||||
RED.menu.setSelected("menu-item-sidebar",true);
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
|
||||
|
||||
sidebarSeparator.width = $("#sidebar").width();
|
||||
},
|
||||
drag: function(event,ui) {
|
||||
var d = ui.position.left-sidebarSeparator.start;
|
||||
var newSidebarWidth = sidebarSeparator.width-d;
|
||||
if (sidebarSeparator.opening) {
|
||||
newSidebarWidth -= 13;
|
||||
newSidebarWidth -= 3;
|
||||
}
|
||||
|
||||
|
||||
if (newSidebarWidth > 150) {
|
||||
if (sidebarSeparator.chartWidth+d < 200) {
|
||||
ui.position.left = 200+sidebarSeparator.start-sidebarSeparator.chartWidth;
|
||||
@@ -79,7 +129,7 @@ RED.sidebar = (function() {
|
||||
newSidebarWidth = sidebarSeparator.width-d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (newSidebarWidth < 150) {
|
||||
if (!sidebarSeparator.closing) {
|
||||
$("#sidebar").addClass("closing");
|
||||
@@ -97,29 +147,26 @@ RED.sidebar = (function() {
|
||||
|
||||
var newChartRight = sidebarSeparator.chartRight-d;
|
||||
$("#workspace").css("right",newChartRight);
|
||||
$("#chart-zoom-controls").css("right",newChartRight+20);
|
||||
$("#sidebar").width(newSidebarWidth);
|
||||
|
||||
sidebar_tabs.resize();
|
||||
RED.view.resize();
|
||||
|
||||
RED.events.emit("sidebar:resize");
|
||||
},
|
||||
stop:function(event,ui) {
|
||||
RED.view.resize();
|
||||
if (sidebarSeparator.closing) {
|
||||
$("#sidebar").removeClass("closing");
|
||||
RED.menu.setSelected("btn-sidebar",false);
|
||||
RED.menu.setSelected("menu-item-sidebar",false);
|
||||
if ($("#sidebar").width() < 180) {
|
||||
$("#sidebar").width(180);
|
||||
$("#workspace").css("right",208);
|
||||
$("#chart-zoom-controls").css("right",228);
|
||||
$("#workspace").css("right",187);
|
||||
}
|
||||
}
|
||||
$("#sidebar-separator").css("left","auto");
|
||||
$("#sidebar-separator").css("right",($("#sidebar").width()+13)+"px");
|
||||
$("#sidebar-separator").css("right",($("#sidebar").width()+2)+"px");
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function toggleSidebar(state) {
|
||||
if (!state) {
|
||||
$("#main-container").addClass("sidebar-closed");
|
||||
@@ -127,31 +174,41 @@ RED.sidebar = (function() {
|
||||
$("#main-container").removeClass("sidebar-closed");
|
||||
sidebar_tabs.resize();
|
||||
}
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
|
||||
|
||||
function showSidebar(id) {
|
||||
if (id) {
|
||||
sidebar_tabs.activateTab("tab-"+id);
|
||||
if (!containsTab(id)) {
|
||||
sidebar_tabs.addTab(knownTabs[id]);
|
||||
}
|
||||
sidebar_tabs.activateTab(id);
|
||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
||||
RED.menu.setSelected("menu-item-sidebar",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function containsTab(id) {
|
||||
return sidebar_tabs.contains("tab-"+id);
|
||||
return sidebar_tabs.contains(id);
|
||||
}
|
||||
|
||||
|
||||
function init () {
|
||||
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("btn-sidebar",!RED.menu.isSelected("btn-sidebar"));d3.event.preventDefault();});
|
||||
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("menu-item-sidebar",!RED.menu.isSelected("menu-item-sidebar"));d3.event.preventDefault();});
|
||||
showSidebar();
|
||||
RED.sidebar.info.show();
|
||||
RED.sidebar.info.init();
|
||||
RED.sidebar.config.init();
|
||||
// hide info bar at start if screen rather narrow...
|
||||
if ($(window).width() < 600) { toggleSidebar(); }
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
init: init,
|
||||
addTab: addTab,
|
||||
removeTab: removeTab,
|
||||
show: showSidebar,
|
||||
containsTab: containsTab,
|
||||
toggleSidebar: toggleSidebar
|
||||
toggleSidebar: toggleSidebar,
|
||||
}
|
||||
|
||||
|
||||
})();
|
608
editor/js/ui/subflow.js
Normal file
@@ -0,0 +1,608 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.subflow = (function() {
|
||||
|
||||
|
||||
function getSubflow() {
|
||||
return RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||
var pos = {x:50,y:30};
|
||||
if (!isInput) {
|
||||
pos.x += 110;
|
||||
}
|
||||
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
|
||||
var port;
|
||||
if (i < subflow.out.length) {
|
||||
port = subflow.out[i];
|
||||
} else {
|
||||
port = subflow.in[i-subflow.out.length];
|
||||
}
|
||||
if (port.x == pos.x && port.y == pos.y) {
|
||||
pos.x += 55;
|
||||
i=0;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
function addSubflowInput() {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow.in.length === 1) {
|
||||
return;
|
||||
}
|
||||
var position = findAvailableSubflowIOPosition(subflow,true);
|
||||
var newInput = {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
z:subflow.id,
|
||||
i:subflow.in.length,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
id:RED.nodes.id()
|
||||
};
|
||||
var oldInCount = subflow.in.length;
|
||||
subflow.in.push(newInput);
|
||||
subflow.dirty = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
var result = refresh(true);
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
inputCount: oldInCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-input-add").addClass("active");
|
||||
$("#workspace-subflow-input-remove").removeClass("active");
|
||||
}
|
||||
|
||||
function removeSubflowInput() {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.in.length === 0) {
|
||||
return;
|
||||
}
|
||||
var removedInput = activeSubflow.in[0];
|
||||
var removedInputLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow" && l.source.z == activeSubflow.id && l.source.i == removedInput.i) {
|
||||
removedInputLinks.push(l);
|
||||
} else if (l.target.type == "subflow:"+activeSubflow.id) {
|
||||
removedInputLinks.push(l);
|
||||
}
|
||||
});
|
||||
removedInputLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
activeSubflow.in = [];
|
||||
$("#workspace-subflow-input-add").removeClass("active");
|
||||
$("#workspace-subflow-input-remove").addClass("active");
|
||||
activeSubflow.changed = true;
|
||||
return {subflowInputs: [ removedInput ], links:removedInputLinks};
|
||||
}
|
||||
|
||||
function addSubflowOutput(id) {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var position = findAvailableSubflowIOPosition(subflow,false);
|
||||
|
||||
var newOutput = {
|
||||
type:"subflow",
|
||||
direction:"out",
|
||||
z:subflow.id,
|
||||
i:subflow.out.length,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
id:RED.nodes.id()
|
||||
};
|
||||
var oldOutCount = subflow.out.length;
|
||||
subflow.out.push(newOutput);
|
||||
subflow.dirty = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
|
||||
var result = refresh(true);
|
||||
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
outputCount: oldOutCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-output .spinner-value").html(subflow.out.length);
|
||||
}
|
||||
|
||||
function removeSubflowOutput(removedSubflowOutputs) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.out.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (typeof removedSubflowOutputs === "undefined") {
|
||||
removedSubflowOutputs = [activeSubflow.out[activeSubflow.out.length-1]];
|
||||
}
|
||||
var removedLinks = [];
|
||||
removedSubflowOutputs.sort(function(a,b) { return b.i-a.i});
|
||||
for (i=0;i<removedSubflowOutputs.length;i++) {
|
||||
var output = removedSubflowOutputs[i];
|
||||
activeSubflow.out.splice(output.i,1);
|
||||
var subflowRemovedLinks = [];
|
||||
var subflowMovedLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.target.type == "subflow" && l.target.z == activeSubflow.id && l.target.i == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
}
|
||||
if (l.source.type == "subflow:"+activeSubflow.id) {
|
||||
if (l.sourcePort == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
} else if (l.sourcePort > output.i) {
|
||||
subflowMovedLinks.push(l);
|
||||
}
|
||||
}
|
||||
});
|
||||
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
subflowMovedLinks.forEach(function(l) { l.sourcePort--; });
|
||||
|
||||
removedLinks = removedLinks.concat(subflowRemovedLinks);
|
||||
for (var j=output.i;j<activeSubflow.out.length;j++) {
|
||||
activeSubflow.out[j].i--;
|
||||
activeSubflow.out[j].dirty = true;
|
||||
}
|
||||
}
|
||||
activeSubflow.changed = true;
|
||||
|
||||
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
||||
}
|
||||
|
||||
function refresh(markChange) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
refreshToolbar(activeSubflow);
|
||||
var subflowInstances = [];
|
||||
if (activeSubflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
|
||||
subflowInstances.push({
|
||||
id: n.id,
|
||||
changed: n.changed
|
||||
});
|
||||
if (markChange) {
|
||||
n.changed = true;
|
||||
}
|
||||
n.inputs = activeSubflow.in.length;
|
||||
n.outputs = activeSubflow.out.length;
|
||||
while (n.outputs < n.ports.length) {
|
||||
n.ports.pop();
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
RED.editor.validateNode(activeSubflow);
|
||||
return {
|
||||
instances: subflowInstances
|
||||
}
|
||||
}
|
||||
}
|
||||
function refreshToolbar(activeSubflow) {
|
||||
if (activeSubflow) {
|
||||
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
||||
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
||||
|
||||
$("#workspace-subflow-output .spinner-value").html(activeSubflow.out.length);
|
||||
}
|
||||
}
|
||||
|
||||
function showWorkspaceToolbar(activeSubflow) {
|
||||
var toolbar = $("#workspace-toolbar");
|
||||
toolbar.empty();
|
||||
|
||||
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
||||
'<div style="display: inline-block;" class="button-group">'+
|
||||
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
||||
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
||||
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
||||
'<div class="spinner-value">3</div>'+
|
||||
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
||||
toolbar.i18n();
|
||||
|
||||
|
||||
$("#workspace-subflow-output-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
var result = removeSubflowOutput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
subflowOutputs: result.subflowOutputs,
|
||||
changed: wasChanged,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
$("#workspace-subflow-output-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowOutput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-input-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowInput();
|
||||
});
|
||||
$("#workspace-subflow-input-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
activeSubflow.changed = true;
|
||||
var result = removeSubflowInput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
changed: wasChanged,
|
||||
subflowInputs: result.subflowInputs,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#workspace-subflow-edit").click(function(event) {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-delete").click(function(event) {
|
||||
event.preventDefault();
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var startDirty = RED.nodes.dirty();
|
||||
|
||||
var activeSubflow = getSubflow();
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function(n) {
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
var removedConfigNodes = [];
|
||||
for (var i=0;i<removedNodes.length;i++) {
|
||||
var removedEntities = RED.nodes.remove(removedNodes[i].id);
|
||||
removedLinks = removedLinks.concat(removedEntities.links);
|
||||
removedConfigNodes = removedConfigNodes.concat(removedEntities.nodes);
|
||||
}
|
||||
// TODO: this whole delete logic should be in RED.nodes.removeSubflow..
|
||||
removedNodes = removedNodes.concat(removedConfigNodes);
|
||||
|
||||
RED.nodes.removeSubflow(activeSubflow);
|
||||
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflow: {
|
||||
subflow: activeSubflow
|
||||
},
|
||||
dirty:startDirty
|
||||
});
|
||||
|
||||
RED.workspaces.remove(activeSubflow);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
refreshToolbar(activeSubflow);
|
||||
|
||||
$("#chart").css({"margin-top": "40px"});
|
||||
$("#workspace-toolbar").show();
|
||||
}
|
||||
function hideWorkspaceToolbar() {
|
||||
$("#workspace-toolbar").hide().empty();
|
||||
$("#chart").css({"margin-top": "0"});
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
RED.events.on("workspace:change",function(event) {
|
||||
var activeSubflow = RED.nodes.subflow(event.workspace);
|
||||
if (activeSubflow) {
|
||||
showWorkspaceToolbar(activeSubflow);
|
||||
} else {
|
||||
hideWorkspaceToolbar();
|
||||
}
|
||||
});
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function createSubflow() {
|
||||
var lastIndex = 0;
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name);
|
||||
if (m) {
|
||||
lastIndex = Math.max(lastIndex,m[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var name = "Subflow "+(lastIndex+1);
|
||||
|
||||
var subflowId = RED.nodes.id();
|
||||
var subflow = {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
info:"",
|
||||
in: [],
|
||||
out: []
|
||||
};
|
||||
RED.nodes.addSubflow(subflow);
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
subflow: {
|
||||
subflow:subflow
|
||||
},
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.workspaces.show(subflowId);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes) {
|
||||
RED.notify(RED._("subflow.errors.noNodesSelected"),"error");
|
||||
return;
|
||||
}
|
||||
var i;
|
||||
var nodes = {};
|
||||
var new_links = [];
|
||||
var removedLinks = [];
|
||||
|
||||
var candidateInputs = [];
|
||||
var candidateOutputs = [];
|
||||
var candidateInputNodes = {};
|
||||
|
||||
|
||||
var boundingBox = [selection.nodes[0].x,
|
||||
selection.nodes[0].y,
|
||||
selection.nodes[0].x,
|
||||
selection.nodes[0].y];
|
||||
|
||||
for (i=0;i<selection.nodes.length;i++) {
|
||||
var n = selection.nodes[i];
|
||||
nodes[n.id] = {n:n,outputs:{}};
|
||||
boundingBox = [
|
||||
Math.min(boundingBox[0],n.x),
|
||||
Math.min(boundingBox[1],n.y),
|
||||
Math.max(boundingBox[2],n.x),
|
||||
Math.max(boundingBox[3],n.y)
|
||||
]
|
||||
}
|
||||
|
||||
var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2];
|
||||
|
||||
RED.nodes.eachLink(function(link) {
|
||||
if (nodes[link.source.id] && nodes[link.target.id]) {
|
||||
// A link wholely within the selection
|
||||
}
|
||||
|
||||
if (nodes[link.source.id] && !nodes[link.target.id]) {
|
||||
// An outbound link from the selection
|
||||
candidateOutputs.push(link);
|
||||
removedLinks.push(link);
|
||||
}
|
||||
if (!nodes[link.source.id] && nodes[link.target.id]) {
|
||||
// An inbound link
|
||||
candidateInputs.push(link);
|
||||
candidateInputNodes[link.target.id] = link.target;
|
||||
removedLinks.push(link);
|
||||
}
|
||||
});
|
||||
|
||||
var outputs = {};
|
||||
candidateOutputs = candidateOutputs.filter(function(v) {
|
||||
if (outputs[v.source.id+":"+v.sourcePort]) {
|
||||
outputs[v.source.id+":"+v.sourcePort].targets.push(v.target);
|
||||
return false;
|
||||
}
|
||||
v.targets = [];
|
||||
v.targets.push(v.target);
|
||||
outputs[v.source.id+":"+v.sourcePort] = v;
|
||||
return true;
|
||||
});
|
||||
candidateOutputs.sort(function(a,b) { return a.source.y-b.source.y});
|
||||
|
||||
if (Object.keys(candidateInputNodes).length > 1) {
|
||||
RED.notify(RED._("subflow.errors.multipleInputsToSelection"),"error");
|
||||
return;
|
||||
}
|
||||
|
||||
var lastIndex = 0;
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name);
|
||||
if (m) {
|
||||
lastIndex = Math.max(lastIndex,m[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var name = "Subflow "+(lastIndex+1);
|
||||
|
||||
var subflowId = RED.nodes.id();
|
||||
var subflow = {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
info:"",
|
||||
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
|
||||
y:candidateInputNodes[v].y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:candidateInputNodes[v].id}]
|
||||
}}),
|
||||
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:v.source.x+(v.source.w/2)+80,
|
||||
y:v.source.y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:v.source.id,port:v.sourcePort}]
|
||||
}})
|
||||
};
|
||||
|
||||
RED.nodes.addSubflow(subflow);
|
||||
|
||||
var subflowInstance = {
|
||||
id:RED.nodes.id(),
|
||||
type:"subflow:"+subflow.id,
|
||||
x: center[0],
|
||||
y: center[1],
|
||||
z: RED.workspaces.active(),
|
||||
inputs: subflow.in.length,
|
||||
outputs: subflow.out.length,
|
||||
h: Math.max(30/*node_height*/,(subflow.out.length||0) * 15),
|
||||
changed:true
|
||||
}
|
||||
subflowInstance._def = RED.nodes.getType(subflowInstance.type);
|
||||
RED.editor.validateNode(subflowInstance);
|
||||
RED.nodes.add(subflowInstance);
|
||||
|
||||
candidateInputs.forEach(function(l) {
|
||||
var link = {source:l.source, sourcePort:l.sourcePort, target: subflowInstance};
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
|
||||
candidateOutputs.forEach(function(output,i) {
|
||||
output.targets.forEach(function(target) {
|
||||
var link = {source:subflowInstance, sourcePort:i, target: target};
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
|
||||
subflow.in.forEach(function(input) {
|
||||
input.wires.forEach(function(wire) {
|
||||
var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) }
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
subflow.out.forEach(function(output,i) {
|
||||
output.wires.forEach(function(wire) {
|
||||
var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output }
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
|
||||
for (i=0;i<removedLinks.length;i++) {
|
||||
RED.nodes.removeLink(removedLinks[i]);
|
||||
}
|
||||
|
||||
for (i=0;i<selection.nodes.length;i++) {
|
||||
selection.nodes[i].z = subflow.id;
|
||||
}
|
||||
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
nodes:[subflowInstance.id],
|
||||
links:new_links,
|
||||
subflow: {
|
||||
subflow: subflow
|
||||
},
|
||||
|
||||
activeWorkspace: RED.workspaces.active(),
|
||||
removedLinks: removedLinks,
|
||||
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
|
||||
RED.editor.validateNode(subflow);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
init: init,
|
||||
createSubflow: createSubflow,
|
||||
convertToSubflow: convertToSubflow,
|
||||
refresh: refresh,
|
||||
removeInput: removeSubflowInput,
|
||||
removeOutput: removeSubflowOutput
|
||||
}
|
||||
})();
|
293
editor/js/ui/tab-config.js
Normal file
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.sidebar.config = (function() {
|
||||
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.className = "sidebar-node-config"
|
||||
|
||||
$('<div class="button-group sidebar-header">'+
|
||||
'<a class="sidebar-header-button-toggle selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
|
||||
'<a class="sidebar-header-button-toggle" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
|
||||
'</div>'
|
||||
).appendTo(content);
|
||||
|
||||
|
||||
var toolbar = $('<div>'+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-collapse-all" href="#"><i class="fa fa-angle-double-up"></i></a> '+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-expand-all" href="#"><i class="fa fa-angle-double-down"></i></a>'+
|
||||
'</div>');
|
||||
|
||||
var globalCategories = $("<div>").appendTo(content);
|
||||
var flowCategories = $("<div>").appendTo(content);
|
||||
var subflowCategories = $("<div>").appendTo(content);
|
||||
|
||||
var showUnusedOnly = false;
|
||||
|
||||
var categories = {};
|
||||
|
||||
function getOrCreateCategory(name,parent,label) {
|
||||
name = name.replace(/\./i,"-");
|
||||
if (!categories[name]) {
|
||||
var container = $('<div class="palette-category workspace-config-node-category" id="workspace-config-node-category-'+name+'"></div>').appendTo(parent);
|
||||
var header = $('<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
|
||||
if (label) {
|
||||
$('<span class="config-node-label"/>').text(label).appendTo(header);
|
||||
} else {
|
||||
$('<span class="config-node-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
|
||||
}
|
||||
$('<span class="config-node-filter-info"></span>').appendTo(header);
|
||||
category = $('<ul class="palette-content config-node-list"></ul>').appendTo(container);
|
||||
container.i18n();
|
||||
var icon = header.find("i");
|
||||
var result = {
|
||||
label: label,
|
||||
list: category,
|
||||
size: function() {
|
||||
return result.list.find("li:not(.config_node_none)").length
|
||||
},
|
||||
open: function(snap) {
|
||||
if (!icon.hasClass("expanded")) {
|
||||
icon.addClass("expanded");
|
||||
if (snap) {
|
||||
result.list.show();
|
||||
} else {
|
||||
result.list.slideDown();
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function(snap) {
|
||||
if (icon.hasClass("expanded")) {
|
||||
icon.removeClass("expanded");
|
||||
if (snap) {
|
||||
result.list.hide();
|
||||
} else {
|
||||
result.list.slideUp();
|
||||
}
|
||||
}
|
||||
},
|
||||
isOpen: function() {
|
||||
return icon.hasClass("expanded");
|
||||
}
|
||||
};
|
||||
|
||||
header.on('click', function(e) {
|
||||
if (result.isOpen()) {
|
||||
result.close();
|
||||
} else {
|
||||
result.open();
|
||||
}
|
||||
});
|
||||
categories[name] = result;
|
||||
} else {
|
||||
if (categories[name].label !== label) {
|
||||
categories[name].list.parent().find('.config-node-label').text(label);
|
||||
categories[name].label = label;
|
||||
}
|
||||
}
|
||||
return categories[name];
|
||||
}
|
||||
|
||||
function createConfigNodeList(id,nodes) {
|
||||
var category = getOrCreateCategory(id.replace(/\./i,"-"))
|
||||
var list = category.list;
|
||||
|
||||
nodes.sort(function(A,B) {
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
return 0;
|
||||
});
|
||||
if (showUnusedOnly) {
|
||||
var hiddenCount = nodes.length;
|
||||
nodes = nodes.filter(function(n) {
|
||||
return n.users.length === 0;
|
||||
})
|
||||
hiddenCount = hiddenCount - nodes.length;
|
||||
if (hiddenCount > 0) {
|
||||
list.parent().find('.config-node-filter-info').text(RED._('sidebar.config.filtered',{count:hiddenCount})).show();
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
list.empty();
|
||||
if (nodes.length === 0) {
|
||||
$('<li class="config_node_none" data-i18n="sidebar.config.none">NONE</li>').i18n().appendTo(list);
|
||||
category.close(true);
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
if (node.type != currentType) {
|
||||
$('<li class="config_node_type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
}
|
||||
|
||||
var entry = $('<li class="palette_node config_node"></li>').appendTo(list);
|
||||
$('<div class="palette_label"></div>').text(label).appendTo(entry);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container palette_icon_container_right"}).text(node.users.length).appendTo(entry);
|
||||
if (node.users.length === 0) {
|
||||
entry.addClass("config_node_unused");
|
||||
}
|
||||
entry.on('click',function(e) {
|
||||
RED.sidebar.info.refresh(node);
|
||||
});
|
||||
entry.on('dblclick',function(e) {
|
||||
RED.editor.editConfig("", node.type, node.id);
|
||||
});
|
||||
var userArray = node.users.map(function(n) { return n.id });
|
||||
entry.on('mouseover',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( userArray.indexOf(node.id) != -1) {
|
||||
node.highlighted = true;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
entry.on('mouseout',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if(node.highlighted) {
|
||||
node.highlighted = false;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
});
|
||||
category.open(true);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshConfigNodeList() {
|
||||
var validList = {"global":true};
|
||||
|
||||
getOrCreateCategory("global",globalCategories);
|
||||
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
validList[ws.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(ws.id,flowCategories,ws.label);
|
||||
})
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
validList[sf.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(sf.id,subflowCategories,sf.name);
|
||||
})
|
||||
$(".workspace-config-node-category").each(function() {
|
||||
var id = $(this).attr('id').substring("workspace-config-node-category-".length);
|
||||
if (!validList[id]) {
|
||||
$(this).remove();
|
||||
delete categories[id];
|
||||
}
|
||||
})
|
||||
var globalConfigNodes = [];
|
||||
var configList = {};
|
||||
RED.nodes.eachConfig(function(cn) {
|
||||
if (cn.z) {//} == RED.workspaces.active()) {
|
||||
configList[cn.z.replace(/\./g,"-")] = configList[cn.z.replace(/\./g,"-")]||[];
|
||||
configList[cn.z.replace(/\./g,"-")].push(cn);
|
||||
} else if (!cn.z) {
|
||||
globalConfigNodes.push(cn);
|
||||
}
|
||||
});
|
||||
for (var id in validList) {
|
||||
if (validList.hasOwnProperty(id)) {
|
||||
createConfigNodeList(id,configList[id]||[]);
|
||||
}
|
||||
}
|
||||
createConfigNodeList('global',globalConfigNodes);
|
||||
}
|
||||
|
||||
function init() {
|
||||
RED.sidebar.addTab({
|
||||
id: "config",
|
||||
label: RED._("sidebar.config.label"),
|
||||
name: RED._("sidebar.config.name"),
|
||||
content: content,
|
||||
toolbar: toolbar,
|
||||
closeable: true,
|
||||
visible: false,
|
||||
onchange: function() { refreshConfigNodeList(); }
|
||||
});
|
||||
|
||||
RED.menu.setAction('menu-item-config-nodes',function() {
|
||||
RED.sidebar.show('config');
|
||||
})
|
||||
|
||||
$("#workspace-config-node-collapse-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
categories[cat].close();
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#workspace-config-node-expand-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
if (categories[cat].size() > 0) {
|
||||
categories[cat].open();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-all').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-unused').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-unused').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (!showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-all').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function show(unused) {
|
||||
if (unused !== undefined) {
|
||||
if (unused) {
|
||||
$('#workspace-config-node-filter-unused').click();
|
||||
} else {
|
||||
$('#workspace-config-node-filter-all').click();
|
||||
}
|
||||
}
|
||||
refreshConfigNodeList();
|
||||
RED.sidebar.show("config");
|
||||
}
|
||||
return {
|
||||
init:init,
|
||||
show:show,
|
||||
refresh:refreshConfigNodeList
|
||||
}
|
||||
})();
|
205
editor/js/ui/tab-info.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.sidebar.info = (function() {
|
||||
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.style.paddingTop = "4px";
|
||||
content.style.paddingLeft = "4px";
|
||||
content.style.paddingRight = "4px";
|
||||
content.className = "sidebar-node-info"
|
||||
|
||||
var propertiesExpanded = false;
|
||||
|
||||
function init() {
|
||||
RED.sidebar.addTab({
|
||||
id: "info",
|
||||
label: RED._("sidebar.info.label"),
|
||||
name: RED._("sidebar.info.name"),
|
||||
content: content
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function show() {
|
||||
RED.sidebar.show("info");
|
||||
}
|
||||
|
||||
function jsonFilter(key,value) {
|
||||
if (key === "") {
|
||||
return value;
|
||||
}
|
||||
var t = typeof value;
|
||||
if ($.isArray(value)) {
|
||||
return "[array:"+value.length+"]";
|
||||
} else if (t === "object") {
|
||||
return "[object]"
|
||||
} else if (t === "string") {
|
||||
if (value.length > 30) {
|
||||
return value.substring(0,30)+" ...";
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function refresh(node) {
|
||||
var table = '<table class="node-info"><tbody>';
|
||||
table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>';
|
||||
if (node.type != "subflow" && node.name) {
|
||||
table += "<tr><td>"+RED._("common.label.name")+"</td><td> "+node.name+"</td></tr>";
|
||||
}
|
||||
table += "<tr><td>"+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>";
|
||||
table += "<tr><td>"+RED._("sidebar.info.id")+"</td><td> "+node.id+"</td></tr>";
|
||||
|
||||
var m = /^subflow(:(.+))?$/.exec(node.type);
|
||||
var subflowNode;
|
||||
if (m) {
|
||||
if (m[2]) {
|
||||
subflowNode = RED.nodes.subflow(m[2]);
|
||||
} else {
|
||||
subflowNode = node;
|
||||
}
|
||||
|
||||
table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.subflow")+'</td></tr>';
|
||||
|
||||
var userCount = 0;
|
||||
var subflowType = "subflow:"+subflowNode.id;
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type === subflowType) {
|
||||
userCount++;
|
||||
}
|
||||
});
|
||||
table += "<tr><td>"+RED._("common.label.name")+"</td><td>"+subflowNode.name+"</td></tr>";
|
||||
table += "<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>";
|
||||
}
|
||||
|
||||
if (!m && node.type != "subflow" && node.type != "comment") {
|
||||
table += '<tr class="blank"><td colspan="2"><a href="#" class="node-info-property-header"><i style="width: 10px; text-align: center;" class="fa fa-caret-'+(propertiesExpanded?"down":"right")+'"></i> '+RED._("sidebar.info.properties")+'</a></td></tr>';
|
||||
if (node._def) {
|
||||
for (var n in node._def.defaults) {
|
||||
if (n != "name" && node._def.defaults.hasOwnProperty(n)) {
|
||||
var val = node[n]||"";
|
||||
var type = typeof val;
|
||||
if (type === "string") {
|
||||
if (val.length === 0) {
|
||||
val += '<span style="font-style: italic; color: #ccc;">'+RED._("sidebar.info.blank")+'</span>';
|
||||
} else {
|
||||
if (val.length > 30) {
|
||||
val = val.substring(0,30)+" ...";
|
||||
}
|
||||
val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
} else if (type === "number") {
|
||||
val = val.toString();
|
||||
} else if ($.isArray(val)) {
|
||||
val = "[<br/>";
|
||||
for (var i=0;i<Math.min(node[n].length,10);i++) {
|
||||
var vv = JSON.stringify(node[n][i],jsonFilter," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
val += " "+i+": "+vv+"<br/>";
|
||||
}
|
||||
if (node[n].length > 10) {
|
||||
val += " ... "+RED._("sidebar.info.arrayItems",{count:node[n].length})+"<br/>";
|
||||
}
|
||||
val += "]";
|
||||
} else {
|
||||
val = JSON.stringify(val,jsonFilter," ");
|
||||
val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
|
||||
table += '<tr class="node-info-property-row'+(propertiesExpanded?"":" hide")+'"><td>'+n+"</td><td>"+val+"</td></tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table += "</tbody></table><hr/>";
|
||||
if (!subflowNode && node.type != "comment") {
|
||||
var helpText = $("script[data-help-name|='"+node.type+"']").html()||"";
|
||||
table += '<div class="node-help">'+helpText+"</div>";
|
||||
}
|
||||
if (subflowNode) {
|
||||
table += '<div class="node-help">'+marked(subflowNode.info||"")+'</div>';
|
||||
} else if (node._def && node._def.info) {
|
||||
var info = node._def.info;
|
||||
table += '<div class="node-help">'+marked(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
//table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
}
|
||||
|
||||
$(content).html(table);
|
||||
|
||||
$(".node-info-property-header").click(function(e) {
|
||||
var icon = $(this).find("i");
|
||||
if (icon.hasClass("fa-caret-right")) {
|
||||
icon.removeClass("fa-caret-right");
|
||||
icon.addClass("fa-caret-down");
|
||||
$(".node-info-property-row").show();
|
||||
propertiesExpanded = true;
|
||||
} else {
|
||||
icon.addClass("fa-caret-right");
|
||||
icon.removeClass("fa-caret-down");
|
||||
$(".node-info-property-row").hide();
|
||||
propertiesExpanded = false;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
function clear() {
|
||||
$(content).html("");
|
||||
}
|
||||
|
||||
function set(html) {
|
||||
$(content).html(html);
|
||||
}
|
||||
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length == 1) {
|
||||
var node = selection.nodes[0];
|
||||
if (node.type === "subflow" && node.direction) {
|
||||
refresh(RED.nodes.subflow(node.z));
|
||||
} else {
|
||||
refresh(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow) {
|
||||
refresh(subflow);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
init: init,
|
||||
show: show,
|
||||
refresh:refresh,
|
||||
clear: clear,
|
||||
set: set
|
||||
}
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,58 +13,90 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
|
||||
|
||||
RED.tabs = (function() {
|
||||
|
||||
|
||||
|
||||
|
||||
function createTabs(options) {
|
||||
var tabs = {};
|
||||
|
||||
var currentTabWidth;
|
||||
var currentActiveTabWidth = 0;
|
||||
|
||||
var ul = $("#"+options.id)
|
||||
ul.addClass("red-ui-tabs");
|
||||
ul.children().first().addClass("active");
|
||||
ul.children().addClass("red-ui-tab");
|
||||
|
||||
|
||||
function onTabClick() {
|
||||
activateTab($(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function onTabDblClick() {
|
||||
if (options.ondblclick) {
|
||||
options.ondblclick(tabs[$(this).attr('href').slice(1)]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function activateTab(link) {
|
||||
if (typeof link === "string") {
|
||||
link = ul.find("a[href='#"+link+"']");
|
||||
}
|
||||
if (!link.parent().hasClass("active")) {
|
||||
ul.children().removeClass("active");
|
||||
ul.children().css({"transition": "width 100ms"});
|
||||
link.parent().addClass("active");
|
||||
if (options.onchange) {
|
||||
options.onchange(tabs[link.attr('href').slice(1)]);
|
||||
}
|
||||
updateTabWidths();
|
||||
setTimeout(function() {
|
||||
ul.children().css({"transition": ""});
|
||||
},100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateTabWidths() {
|
||||
var tabs = ul.find("li.red-ui-tab");
|
||||
var width = ul.width();
|
||||
var tabCount = tabs.size();
|
||||
var tabWidth = (width-6-(tabCount*7))/tabCount;
|
||||
var pct = 100*tabWidth/width;
|
||||
tabs.css({width:pct+"%"});
|
||||
var tabWidth = (width-12-(tabCount*6))/tabCount;
|
||||
currentTabWidth = 100*tabWidth/width;
|
||||
currentActiveTabWidth = currentTabWidth+"%";
|
||||
|
||||
if (options.hasOwnProperty("minimumActiveTabWidth")) {
|
||||
if (tabWidth < options.minimumActiveTabWidth) {
|
||||
tabCount -= 1;
|
||||
tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount;
|
||||
currentTabWidth = 100*tabWidth/width;
|
||||
currentActiveTabWidth = options.minimumActiveTabWidth+"px";
|
||||
} else {
|
||||
currentActiveTabWidth = 0;
|
||||
}
|
||||
}
|
||||
tabs.css({width:currentTabWidth+"%"});
|
||||
if (tabWidth < 50) {
|
||||
ul.find(".red-ui-tab-close").hide();
|
||||
ul.find(".red-ui-tab-icon").hide();
|
||||
} else {
|
||||
ul.find(".red-ui-tab-close").show();
|
||||
ul.find(".red-ui-tab-icon").show();
|
||||
}
|
||||
if (currentActiveTabWidth !== 0) {
|
||||
ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick);
|
||||
updateTabWidths();
|
||||
|
||||
|
||||
|
||||
|
||||
function removeTab(id) {
|
||||
var li = ul.find("a[href='#"+id+"']").parent();
|
||||
if (li.hasClass("active")) {
|
||||
@@ -81,20 +113,23 @@ RED.tabs = (function() {
|
||||
delete tabs[id];
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
addTab: function(tab) {
|
||||
tabs[tab.id] = tab;
|
||||
var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
|
||||
var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
|
||||
link.html(tab.label);
|
||||
|
||||
if (tab.icon) {
|
||||
$('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
|
||||
}
|
||||
$('<span/>').text(tab.label).appendTo(link);
|
||||
|
||||
link.on("click",onTabClick);
|
||||
link.on("dblclick",onTabDblClick);
|
||||
if (tab.closeable) {
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
closeLink.html('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.append('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.on("click",function(event) {
|
||||
removeTab(tab.id);
|
||||
});
|
||||
@@ -121,13 +156,13 @@ RED.tabs = (function() {
|
||||
tabs[id].label = label;
|
||||
var tab = ul.find("a[href='#"+id+"']");
|
||||
tab.attr("title",label);
|
||||
tab.text(label);
|
||||
tab.find("span").text(label);
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
create: createTabs
|
||||
}
|
306
editor/js/ui/typedInput.js
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
var allOptions = {
|
||||
msg: {value:"msg",label:"msg.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
flow: {value:"flow",label:"flow.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
global: {value:"global",label:"global.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"},
|
||||
num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
|
||||
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]},
|
||||
json: {value:"json",label:"JSON",icon:"red/images/typedInput/json.png", validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}},
|
||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.png"}
|
||||
};
|
||||
var nlsd = false;
|
||||
|
||||
$.widget( "nodered.typedInput", {
|
||||
_create: function() {
|
||||
if (!nlsd && RED && RED._) {
|
||||
for (var i in allOptions) {
|
||||
if (allOptions.hasOwnProperty(i)) {
|
||||
allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label});
|
||||
}
|
||||
}
|
||||
}
|
||||
nlsd = true;
|
||||
var that = this;
|
||||
|
||||
this.disarmClick = false;
|
||||
this.element.addClass('red-ui-typedInput');
|
||||
this.uiWidth = this.element.width();
|
||||
this.uiSelect = this.element
|
||||
.wrap( "<div>" )
|
||||
.parent();
|
||||
|
||||
["Right","Left"].forEach(function(d) {
|
||||
var m = that.element.css("margin"+d);
|
||||
that.uiSelect.css("margin"+d,m);
|
||||
that.element.css("margin"+d,0);
|
||||
});
|
||||
this.uiSelect.addClass("red-ui-typedInput-container");
|
||||
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
|
||||
var hasSubOptions = false;
|
||||
this.typeMap = {};
|
||||
this.types = this.options.types.map(function(opt) {
|
||||
var result;
|
||||
if (typeof opt === 'string') {
|
||||
result = allOptions[opt];
|
||||
} else {
|
||||
result = opt;
|
||||
}
|
||||
that.typeMap[result.value] = result;
|
||||
if (result.options) {
|
||||
hasSubOptions = true;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
if (this.options.typeField) {
|
||||
this.typeField = $(this.options.typeField).hide();
|
||||
var t = this.typeField.val();
|
||||
if (t && this.typeMap[t]) {
|
||||
this.options.default = t;
|
||||
}
|
||||
} else {
|
||||
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
|
||||
}
|
||||
|
||||
this.selectTrigger = $('<a href="#"><i class="fa fa-sort-desc"></i></a>').prependTo(this.uiSelect);
|
||||
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
|
||||
|
||||
this.element.on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('blur', function() {
|
||||
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('change', function() {
|
||||
that.validate();
|
||||
})
|
||||
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
that._showMenu(that.menu,that.selectTrigger);
|
||||
});
|
||||
|
||||
|
||||
if (hasSubOptions) {
|
||||
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
|
||||
this.optionSelectTrigger = $('<a href="#" class="red-ui-typedInput-option-trigger" style="display:inline-block"><i class="fa fa-sort-desc"></i></a>').appendTo(this.uiSelect);
|
||||
this.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger);
|
||||
this.optionSelectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
if (that.optionMenu) {
|
||||
that.optionMenu.css({
|
||||
minWidth:that.optionSelectLabel.width()
|
||||
});
|
||||
|
||||
that._showMenu(that.optionMenu,that.optionSelectLabel)
|
||||
}
|
||||
});
|
||||
}
|
||||
this.menu = this._createMenu(this.types, function(v) { that.type(v) });
|
||||
this.type(this.options.default||this.types[0].value);
|
||||
},
|
||||
_hideMenu: function(menu) {
|
||||
$(document).off("mousedown.close-property-select");
|
||||
menu.hide();
|
||||
this.element.focus();
|
||||
},
|
||||
_createMenu: function(opts,callback) {
|
||||
var that = this;
|
||||
var menu = $("<div>").addClass("red-ui-typedInput-options");
|
||||
opts.forEach(function(opt) {
|
||||
if (typeof opt === 'string') {
|
||||
opt = {value:opt,label:opt};
|
||||
}
|
||||
var op = $('<a href="#">').attr("value",opt.value).appendTo(menu);
|
||||
if (opt.label) {
|
||||
op.text(opt.label);
|
||||
}
|
||||
if (opt.icon) {
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
|
||||
} else {
|
||||
op.css({paddingLeft: "18px"});
|
||||
}
|
||||
|
||||
op.click(function(event) {
|
||||
event.preventDefault();
|
||||
callback(opt.value);
|
||||
that._hideMenu(menu);
|
||||
});
|
||||
});
|
||||
menu.css({
|
||||
display: "none",
|
||||
});
|
||||
menu.appendTo(document.body);
|
||||
return menu;
|
||||
|
||||
},
|
||||
_showMenu: function(menu,relativeTo) {
|
||||
if (this.disarmClick) {
|
||||
this.disarmClick = false;
|
||||
return
|
||||
}
|
||||
var that = this;
|
||||
var pos = relativeTo.offset();
|
||||
var height = relativeTo.height();
|
||||
menu.css({
|
||||
top: (height+pos.top-3)+"px",
|
||||
left: (2+pos.left)+"px",
|
||||
});
|
||||
menu.slideDown(100);
|
||||
this._delay(function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
$(document).on("mousedown.close-property-select", function(event) {
|
||||
if(!$(event.target).closest(menu).length) {
|
||||
that._hideMenu(menu);
|
||||
}
|
||||
if ($(event.target).closest(relativeTo).length) {
|
||||
that.disarmClick = true;
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
_getLabelWidth: function(label) {
|
||||
var labelWidth = label.width();
|
||||
if (labelWidth === 0) {
|
||||
var newTrigger = label.clone();
|
||||
newTrigger.css({
|
||||
position:"absolute",
|
||||
top:0,
|
||||
left:-1000
|
||||
}).appendTo(document.body);
|
||||
labelWidth = newTrigger.width()+4;
|
||||
newTrigger.remove();
|
||||
}
|
||||
return labelWidth;
|
||||
},
|
||||
_resize: function() {
|
||||
|
||||
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
|
||||
this.selectTrigger.width(this.uiWidth+5);
|
||||
} else {
|
||||
this.selectTrigger.width('auto');
|
||||
var labelWidth = this._getLabelWidth(this.selectTrigger);
|
||||
|
||||
var newWidth = this.uiWidth-labelWidth+4;
|
||||
this.element.width(newWidth);
|
||||
|
||||
if (this.optionSelectTrigger) {
|
||||
var triggerWidth = this._getLabelWidth(this.optionSelectTrigger);
|
||||
labelWidth = this._getLabelWidth(this.optionSelectLabel)-4;
|
||||
this.optionSelectLabel.width(labelWidth+(newWidth-triggerWidth));
|
||||
}
|
||||
}
|
||||
},
|
||||
_destroy: function() {
|
||||
this.menu.remove();
|
||||
},
|
||||
width: function(desiredWidth) {
|
||||
this.uiWidth = desiredWidth;
|
||||
this._resize();
|
||||
},
|
||||
value: function(value) {
|
||||
if (!arguments.length) {
|
||||
return this.element.val();
|
||||
} else {
|
||||
if (this.typeMap[this.propertyType].options) {
|
||||
if (this.typeMap[this.propertyType].options.indexOf(value) === -1) {
|
||||
value = "";
|
||||
}
|
||||
this.optionSelectLabel.text(value);
|
||||
}
|
||||
this.element.val(value);
|
||||
this.element.trigger('change');
|
||||
}
|
||||
},
|
||||
type: function(type) {
|
||||
if (!arguments.length) {
|
||||
return this.propertyType;
|
||||
} else {
|
||||
var opt = this.typeMap[type];
|
||||
if (opt && this.propertyType !== type) {
|
||||
this.propertyType = type;
|
||||
this.typeField.val(type);
|
||||
this.selectLabel.empty();
|
||||
if (opt.icon) {
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
|
||||
} else {
|
||||
this.selectLabel.text(opt.label);
|
||||
}
|
||||
if (opt.options) {
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.show();
|
||||
this.element.hide();
|
||||
var that = this;
|
||||
this.optionMenu = this._createMenu(opt.options,function(v){
|
||||
that.optionSelectLabel.text(v);
|
||||
that.value(v);
|
||||
});
|
||||
var currentVal = this.element.val();
|
||||
if (opt.options.indexOf(currentVal) !== -1) {
|
||||
this.optionSelectLabel.text(currentVal);
|
||||
} else {
|
||||
this.value(opt.options[0]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.remove();
|
||||
this.optionMenu = null;
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.hide();
|
||||
}
|
||||
if (opt.hasValue === false) {
|
||||
this.element.val("");
|
||||
this.element.hide();
|
||||
} else {
|
||||
this.element.show();
|
||||
}
|
||||
this.element.trigger('change');
|
||||
}
|
||||
this._resize();
|
||||
}
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
var result;
|
||||
var value = this.value();
|
||||
var type = this.type();
|
||||
if (this.typeMap[type] && this.typeMap[type].validate) {
|
||||
var val = this.typeMap[type].validate;
|
||||
if (typeof val === 'function') {
|
||||
result = val(value);
|
||||
} else {
|
||||
result = val.test(value);
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
if (result) {
|
||||
this.uiSelect.removeClass('input-error');
|
||||
} else {
|
||||
this.uiSelect.addClass('input-error');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
257
editor/js/ui/workspaces.js
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.workspaces = (function() {
|
||||
|
||||
var activeWorkspace = 0;
|
||||
var workspaceIndex = 0;
|
||||
|
||||
function addWorkspace(ws) {
|
||||
if (ws) {
|
||||
workspace_tabs.addTab(ws);
|
||||
workspace_tabs.resize();
|
||||
} else {
|
||||
var tabId = RED.nodes.id();
|
||||
do {
|
||||
workspaceIndex += 1;
|
||||
} while($("#workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
||||
|
||||
ws = {type:"tab",id:tabId,label:RED._('workspace.defaultName',{number:workspaceIndex})};
|
||||
RED.nodes.addWorkspace(ws);
|
||||
workspace_tabs.addTab(ws);
|
||||
workspace_tabs.activateTab(tabId);
|
||||
RED.history.push({t:'add',workspaces:[ws],dirty:RED.nodes.dirty()});
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
function deleteWorkspace(ws,force) {
|
||||
if (workspace_tabs.count() == 1) {
|
||||
return;
|
||||
}
|
||||
var nodes = [];
|
||||
if (!force) {
|
||||
nodes = RED.nodes.filterNodes({z:ws.id});
|
||||
}
|
||||
if (force || nodes.length === 0) {
|
||||
removeWorkspace(ws);
|
||||
var historyEvent = RED.nodes.removeWorkspace(ws.id);
|
||||
historyEvent.t = 'delete';
|
||||
historyEvent.dirty = RED.nodes.dirty();
|
||||
historyEvent.workspaces = [ws];
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.dirty(true);
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
$( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws);
|
||||
$( "#node-dialog-delete-workspace-content" ).text(RED._("workspace.delete",{label:ws.label}));
|
||||
$( "#node-dialog-delete-workspace" ).dialog('open');
|
||||
}
|
||||
}
|
||||
function showRenameWorkspaceDialog(id) {
|
||||
var ws = RED.nodes.workspace(id);
|
||||
$( "#node-dialog-rename-workspace" ).dialog("option","workspace",ws);
|
||||
|
||||
if (workspace_tabs.count() == 1) {
|
||||
$( "#node-dialog-rename-workspace").next().find(".leftButton")
|
||||
.prop('disabled',true)
|
||||
.addClass("ui-state-disabled");
|
||||
} else {
|
||||
$( "#node-dialog-rename-workspace").next().find(".leftButton")
|
||||
.prop('disabled',false)
|
||||
.removeClass("ui-state-disabled");
|
||||
}
|
||||
|
||||
$( "#node-input-workspace-name" ).val(ws.label);
|
||||
$( "#node-dialog-rename-workspace" ).dialog("open");
|
||||
}
|
||||
|
||||
var workspace_tabs;
|
||||
function createWorkspaceTabs(){
|
||||
workspace_tabs = RED.tabs.create({
|
||||
id: "workspace-tabs",
|
||||
onchange: function(tab) {
|
||||
var event = {
|
||||
old: activeWorkspace
|
||||
}
|
||||
activeWorkspace = tab.id;
|
||||
event.workspace = activeWorkspace;
|
||||
RED.events.emit("workspace:change",event);
|
||||
RED.sidebar.config.refresh();
|
||||
},
|
||||
ondblclick: function(tab) {
|
||||
if (tab.type != "subflow") {
|
||||
showRenameWorkspaceDialog(tab.id);
|
||||
} else {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(tab.id));
|
||||
}
|
||||
},
|
||||
onadd: function(tab) {
|
||||
RED.menu.addItem("menu-item-workspace",{
|
||||
id:"menu-item-workspace-menu-"+tab.id.replace(".","-"),
|
||||
label:tab.label,
|
||||
onselect:function() {
|
||||
workspace_tabs.activateTab(tab.id);
|
||||
}
|
||||
});
|
||||
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
|
||||
},
|
||||
onremove: function(tab) {
|
||||
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
|
||||
RED.menu.removeItem("menu-item-workspace-menu-"+tab.id.replace(".","-"));
|
||||
},
|
||||
minimumActiveTabWidth: 150
|
||||
});
|
||||
|
||||
|
||||
$("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();});
|
||||
$( "#node-dialog-rename-workspace" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
title: RED._("workspace.renameSheet"),
|
||||
buttons: [
|
||||
{
|
||||
class: 'leftButton',
|
||||
text: RED._("common.label.delete"),
|
||||
click: function() {
|
||||
var workspace = $(this).dialog('option','workspace');
|
||||
$( this ).dialog( "close" );
|
||||
deleteWorkspace(workspace);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
var workspace = $(this).dialog('option','workspace');
|
||||
var label = $( "#node-input-workspace-name" ).val();
|
||||
if (workspace.label != label) {
|
||||
workspace_tabs.renameTab(workspace.id,label);
|
||||
RED.nodes.dirty(true);
|
||||
RED.sidebar.config.refresh();
|
||||
$("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(label);
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
$( "#node-dialog-delete-workspace" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
title: RED._("workspace.confirmDelete"),
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
var workspace = $(this).dialog('option','workspace');
|
||||
deleteWorkspace(workspace,true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
createWorkspaceTabs();
|
||||
$('#btn-workspace-add-tab').on("click",function(e) {addWorkspace(); e.preventDefault()});
|
||||
RED.events.on("sidebar:resize",workspace_tabs.resize);
|
||||
|
||||
RED.menu.setAction('menu-item-workspace-delete',function() {
|
||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||
});
|
||||
|
||||
$(window).resize(function() {
|
||||
workspace_tabs.resize();
|
||||
});
|
||||
}
|
||||
|
||||
function removeWorkspace(ws) {
|
||||
if (!ws) {
|
||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||
} else {
|
||||
if (workspace_tabs.contains(ws.id)) {
|
||||
workspace_tabs.removeTab(ws.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add: addWorkspace,
|
||||
remove: removeWorkspace,
|
||||
|
||||
edit: function(id) {
|
||||
showRenameWorkspaceDialog(id||activeWorkspace);
|
||||
},
|
||||
contains: function(id) {
|
||||
return workspace_tabs.contains(id);
|
||||
},
|
||||
count: function() {
|
||||
return workspace_tabs.count();
|
||||
},
|
||||
active: function() {
|
||||
return activeWorkspace
|
||||
},
|
||||
show: function(id) {
|
||||
if (!workspace_tabs.contains(id)) {
|
||||
var sf = RED.nodes.subflow(id);
|
||||
if (sf) {
|
||||
addWorkspace({type:"subflow",id:id,icon:"red/images/subflow_tab.png",label:sf.name, closeable: true});
|
||||
}
|
||||
}
|
||||
workspace_tabs.activateTab(id);
|
||||
},
|
||||
refresh: function() {
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
if (workspace_tabs.contains(sf.id)) {
|
||||
workspace_tabs.renameTab(sf.id,sf.name);
|
||||
}
|
||||
});
|
||||
RED.sidebar.config.refresh();
|
||||
},
|
||||
resize: function() {
|
||||
workspace_tabs.resize();
|
||||
}
|
||||
}
|
||||
})();
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Copyright 2014, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.user = (function() {
|
||||
|
||||
|
||||
function login(opts,done) {
|
||||
if (typeof opts == 'function') {
|
||||
done = opts;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
|
||||
var dialog = $('<div id="node-dialog-login" class="hide">'+
|
||||
'<div style="display: inline-block;width: 250px; vertical-align: top; margin-right: 10px; margin-bottom: 20px;"><img src="node-red-256.png"/></div>'+
|
||||
'<div style="display: inline-block;width: 250px; vertical-align: top; margin-right: 10px; margin-bottom: 20px;"><img id="node-dialog-login-image" src=""/></div>'+
|
||||
'<div style="display: inline-block; width: 250px; vertical-align: bottom; margin-left: 10px; margin-bottom: 20px;">'+
|
||||
'<form id="node-dialog-login-fields" class="form-horizontal" style="margin-bottom: 0px;"></form>'+
|
||||
'</div>'+
|
||||
@@ -37,7 +37,7 @@ RED.user = (function() {
|
||||
resizable: false,
|
||||
draggable: false
|
||||
});
|
||||
|
||||
|
||||
$("#node-dialog-login-fields").empty();
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
@@ -45,25 +45,48 @@ RED.user = (function() {
|
||||
success: function(data) {
|
||||
if (data.type == "credentials") {
|
||||
var i=0;
|
||||
|
||||
if (data.image) {
|
||||
$("#node-dialog-login-image").attr("src",data.image);
|
||||
} else {
|
||||
$("#node-dialog-login-image").attr("src","red/images/node-red-256.png");
|
||||
}
|
||||
for (;i<data.prompts.length;i++) {
|
||||
var field = data.prompts[i];
|
||||
var row = $("<div/>",{class:"form-row"});
|
||||
var row = $("<div/>",{id:"rrr"+i,class:"form-row"});
|
||||
$('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row);
|
||||
$('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row);
|
||||
var input = $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row);
|
||||
|
||||
if (i<data.prompts.length-1) {
|
||||
input.keypress(
|
||||
(function() {
|
||||
var r = row;
|
||||
return function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
r.next("div").find("input").focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
row.appendTo("#node-dialog-login-fields");
|
||||
}
|
||||
$('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">Login failed</span><img src="spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+
|
||||
(opts.cancelable?'<a href="#" id="node-dialog-login-cancel" style="margin-right: 20px;" tabIndex="'+(i+1)+'">Cancel</a>':'')+
|
||||
'<a href="#" id="node-dialog-login-submit" tabIndex="'+(i+2)+'">Login</a></div>').appendTo("#node-dialog-login-fields");
|
||||
$("#node-dialog-login-submit").button().click(function( event ) {
|
||||
$('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">'+RED._("user.loginFailed")+'</span><img src="red/images/spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+
|
||||
(opts.cancelable?'<a href="#" id="node-dialog-login-cancel" style="margin-right: 20px;" tabIndex="'+(i+1)+'">'+RED._("common.label.cancel")+'</a>':'')+
|
||||
'<input type="submit" id="node-dialog-login-submit" style="width: auto;" tabIndex="'+(i+2)+'" value="'+RED._("user.login")+'"></div>').appendTo("#node-dialog-login-fields");
|
||||
|
||||
|
||||
$("#node-dialog-login-submit").button();
|
||||
$("#node-dialog-login-fields").submit(function(event) {
|
||||
$("#node-dialog-login-submit").button("option","disabled",true);
|
||||
$("#node-dialog-login-failed").hide();
|
||||
$(".login-spinner").show();
|
||||
|
||||
|
||||
var body = {
|
||||
client_id: "node-red-editor",
|
||||
grant_type: "password",
|
||||
scope:"*"
|
||||
scope:""
|
||||
}
|
||||
for (var i=0;i<data.prompts.length;i++) {
|
||||
var field = data.prompts[i];
|
||||
@@ -93,7 +116,7 @@ RED.user = (function() {
|
||||
}
|
||||
}
|
||||
dialog.dialog("open");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,8 +131,57 @@ RED.user = (function() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function updateUserMenu() {
|
||||
$("#usermenu-submenu li").remove();
|
||||
if (RED.settings.user.anonymous) {
|
||||
RED.menu.addItem("btn-usermenu",{
|
||||
id:"usermenu-item-login",
|
||||
label:RED._("menu.label.login"),
|
||||
onselect: function() {
|
||||
RED.user.login({cancelable:true},function() {
|
||||
RED.settings.load(function() {
|
||||
RED.notify(RED._("user.loggedInAs",{name:RED.settings.user.username}),"success");
|
||||
updateUserMenu();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
RED.menu.addItem("btn-usermenu",{
|
||||
id:"usermenu-item-username",
|
||||
label:"<b>"+RED.settings.user.username+"</b>"
|
||||
});
|
||||
RED.menu.addItem("btn-usermenu",{
|
||||
id:"usermenu-item-logout",
|
||||
label:RED._("menu.label.logout"),
|
||||
onselect: function() {
|
||||
RED.user.logout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function init() {
|
||||
if (RED.settings.user) {
|
||||
if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu")) {
|
||||
|
||||
$('<li><a id="btn-usermenu" class="button hide" data-toggle="dropdown" href="#"><i class="fa fa-user"></i></a></li>')
|
||||
.prependTo(".header-toolbar");
|
||||
|
||||
RED.menu.init({id:"btn-usermenu",
|
||||
options: []
|
||||
});
|
||||
updateUserMenu();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return {
|
||||
init: init,
|
||||
login: login,
|
||||
logout: logout
|
||||
}
|
21
editor/sass/bootstrap.scss
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.popover-title { display: none; }
|
||||
|
||||
div.btn-group, a.btn {
|
||||
@include disable-selection;
|
||||
}
|
50
editor/sass/colors.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
$form-placeholder-color: #bbbbbb;
|
||||
$form-text-color: #444;
|
||||
$form-input-focus-color: rgba(85,150,230,0.8);
|
||||
$form-input-border-color: #ccc;
|
||||
|
||||
|
||||
$node-selected-color: #ff7f0e;
|
||||
$port-selected-color: #ff7f0e;
|
||||
$link-color: #888;
|
||||
$link-subflow-color: #bbb;
|
||||
$link-unknown-color: #f00;
|
||||
|
||||
$primary-border-color: #bbbbbb;
|
||||
$secondary-border-color: #dddddd;
|
||||
|
||||
$tab-background-active: #fff;
|
||||
$tab-background-inactive: #f0f0f0;
|
||||
$tab-background-hover: #ddd;
|
||||
|
||||
$palette-header-background: #f3f3f3;
|
||||
|
||||
$workspace-button-background: #fff;
|
||||
$workspace-button-background-hover: #ddd;
|
||||
$workspace-button-background-active: #efefef;
|
||||
$workspace-button-color: #999;
|
||||
$workspace-button-color-disabled: #ccc;
|
||||
$workspace-button-color-focus: #999;
|
||||
$workspace-button-color-hover: #666;
|
||||
$workspace-button-color-active: #666;
|
||||
$workspace-button-color-selected: #AAA;
|
||||
|
||||
$typedInput-button-background: #efefef;
|
||||
$typedInput-button-background-hover: #ddd;
|
||||
$typedInput-button-background-active: #e3e3e3;
|
37
editor/sass/dragdrop.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
#dropTarget {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
left: 0; right: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
display:table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
#dropTarget div {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
color: #fff;
|
||||
}
|
||||
#dropTarget div i {
|
||||
font-size: 80px;
|
||||
}
|
||||
|
84
editor/sass/dropdownMenu.scss
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus, .dropdown-submenu:hover>a, .dropdown-submenu:focus>a {
|
||||
background: #999;
|
||||
}
|
||||
|
||||
.dropdown-menu * .fa-check-square {
|
||||
display: none;
|
||||
color: #e0e0e0;
|
||||
margin-left: -25px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.dropdown-menu * a.active > .fa-check-square {
|
||||
display: inline-block;
|
||||
}
|
||||
.dropdown-menu * .fa-square {
|
||||
display: inline-block;
|
||||
color: #e0e0e0;
|
||||
margin-left: -25px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.dropdown-menu * a.active > .fa-square {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.dropdown-menu>li.disabled>a:hover>[class^="icon-"] {
|
||||
background-image: url("vendor/bootstrap/img/glyphicons-halflings.png") !important;
|
||||
}
|
||||
/** Fix for unreachable dropdown menu **/
|
||||
.dropdown-menu {
|
||||
border-radius: 0;
|
||||
width: 200px !important;
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
.dropdown-menu > li > a > i {
|
||||
width: 10px;
|
||||
text-align: center;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.dropdown-menu > li > a {
|
||||
padding-left: 38px ;
|
||||
text-indent: -8px ;
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
.dropdown-submenu>a:after {
|
||||
display: none;
|
||||
}
|
||||
.dropdown-submenu>a:before {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-top: 5px;
|
||||
margin-left: -30px;
|
||||
border-color: transparent;
|
||||
border-right-color: #e0e0e0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 5px 0;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
.dropdown-submenu.disabled > a:before {
|
||||
border-right-color: #444;
|
||||
}
|
||||
.dropdown-submenu.pull-left>.dropdown-menu {
|
||||
border-radius: 0;
|
||||
}
|
106
editor/sass/editor.scss
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.dialog-form, #dialog-form, #dialog-config-form {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
border-color: rgb(214, 97, 95) !important;
|
||||
}
|
||||
|
||||
.ui-dialog .ui-dialog-buttonpane button.leftButton {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
clear: both;
|
||||
color: $form-text-color;
|
||||
margin-bottom:12px;
|
||||
}
|
||||
.form-row label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
.form-row input {
|
||||
width:70%;
|
||||
}
|
||||
|
||||
.form-tips {
|
||||
background: #ffe;
|
||||
padding: 8px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid $secondary-border-color;
|
||||
max-width: 450px;
|
||||
}
|
||||
.form-tips code {
|
||||
border: none;
|
||||
padding: auto;
|
||||
}
|
||||
.form-tips a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.node-text-editor {
|
||||
border:1px solid #ccc;
|
||||
border-radius:5px;
|
||||
overflow: hidden;
|
||||
font-size: 14px !important;
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
||||
.editor-button {
|
||||
@include workspace-button;
|
||||
height: 34px;
|
||||
line-height: 32px;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.editor-button-small {
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
font-size: 10px;
|
||||
border-radius: 2px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
#node-config-dialog-scope-container {
|
||||
cursor: auto;
|
||||
float: right;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
#node-config-dialog-scope-warning {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
color: #AD1625;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#node-config-dialog-scope {
|
||||
margin: 1px 0 0 0;
|
||||
padding: 0;
|
||||
height: 22px;
|
||||
width: 110px;
|
||||
}
|
||||
#node-config-dialog-user-count {
|
||||
vertical-align: middle;
|
||||
display:inline-block;
|
||||
margin-top: 10px;
|
||||
margin-right: 20px;
|
||||
float:left;
|
||||
font-size: 12px;
|
||||
}
|
239
editor/sass/flow.scss
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lasso {
|
||||
stroke-width: 1px;
|
||||
stroke: #ff7f0e;
|
||||
fill: rgba(20,125,255,0.1);
|
||||
stroke-dasharray: 10 5;
|
||||
}
|
||||
|
||||
.group-box {
|
||||
stroke-width: 1px;
|
||||
stroke: #aaaaaa;
|
||||
fill: rgba(208, 211, 238, 0.1);
|
||||
stroke-dasharray: 3 3;
|
||||
}
|
||||
.group-box-active {
|
||||
fill: #fff;
|
||||
stroke: #ff7f0e;
|
||||
}
|
||||
|
||||
.group_label {
|
||||
stroke-width: 0;
|
||||
fill: #999;
|
||||
font-size: 11px;
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.node_label_italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.node_label_unknown {
|
||||
font-style: italic;
|
||||
fill: #e00 !important;
|
||||
}
|
||||
.node_label_white {
|
||||
fill: #eee !important;
|
||||
}
|
||||
.node_label {
|
||||
stroke-width: 0;
|
||||
fill: #333;
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
.port_label {
|
||||
stroke-width: 0;
|
||||
fill: #888;
|
||||
font-size: 16px;
|
||||
alignment-baseline: middle;
|
||||
text-anchor: middle;
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
|
||||
.function_label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.node {
|
||||
stroke: #999;
|
||||
cursor: move;
|
||||
stroke-width: 1;
|
||||
}
|
||||
.node_unknown {
|
||||
stroke-dasharray:10,4;
|
||||
stroke: #f33;
|
||||
}
|
||||
.tool_arrow {
|
||||
stroke-width: 1;
|
||||
stroke: #999;
|
||||
fill: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
.node_tools {
|
||||
fill: #ddd;
|
||||
stroke: #999;
|
||||
cursor: move;
|
||||
stroke-width: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.node_tools_hovered {
|
||||
stroke: #ff7f0e;
|
||||
fill: #eee;
|
||||
}
|
||||
|
||||
.node_button {
|
||||
fill: inherit;
|
||||
|
||||
}
|
||||
.port {
|
||||
stroke: #999;
|
||||
stroke-width: 1;
|
||||
fill: #ddd;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.port_highlight {
|
||||
stroke: #6DA332;
|
||||
stroke-width: 3;
|
||||
fill: #fff;
|
||||
pointer-events:none;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
.node_error {
|
||||
stroke: #ff0000;
|
||||
stroke-width: 2;
|
||||
fill: #ff7f0e;
|
||||
}
|
||||
|
||||
.node_badge {
|
||||
stroke: rgb(93, 114, 145);
|
||||
stroke-width: 1;
|
||||
fill: rgb(190, 209, 255);
|
||||
}
|
||||
.node_badge_label {
|
||||
stroke-width:0;
|
||||
fill: #fff;
|
||||
font-size: 11px;
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
@include disable-selection;
|
||||
|
||||
}
|
||||
.node_invalid {
|
||||
stroke: #ff0000;
|
||||
}
|
||||
.node_selected {
|
||||
stroke-width: 2;
|
||||
stroke: $node-selected-color !important;
|
||||
}
|
||||
.node_highlighted {
|
||||
stroke: #dd1616;
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
||||
.node_hovered {
|
||||
}
|
||||
|
||||
.node_status_label {
|
||||
@include disable-selection;
|
||||
stroke-width: 0;
|
||||
fill: #888;
|
||||
font-size:9pt;
|
||||
stroke:#000;
|
||||
text-anchor:start;
|
||||
}
|
||||
|
||||
.port_hovered {
|
||||
stroke: $port-selected-color;
|
||||
fill: $port-selected-color;
|
||||
}
|
||||
.subflowport {
|
||||
stroke-dasharray: 5,5;
|
||||
fill: #eee;
|
||||
stroke: #999;
|
||||
}
|
||||
|
||||
.drag_line {
|
||||
stroke: $node-selected-color;
|
||||
stroke-width: 3;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.drag_line_hidden {
|
||||
stroke: $node-selected-color;
|
||||
stroke-width: 0;
|
||||
pointer-events: none;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.link_line {
|
||||
stroke: $link-color;
|
||||
stroke-width: 3;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.link_subflow {
|
||||
stroke: $link-subflow-color;
|
||||
stroke-dasharray: 10,5;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.link_outline {
|
||||
stroke: #fff;
|
||||
stroke-width: 4;
|
||||
cursor: crosshair;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
.link_background {
|
||||
stroke: #fff;
|
||||
opacity: 0;
|
||||
stroke-width: 20;
|
||||
cursor: crosshair;
|
||||
fill: none;
|
||||
}
|
||||
.link_splice > .link_line {
|
||||
stroke-dasharray: 15,8;
|
||||
}
|
||||
|
||||
g.link_selected path.link_line {
|
||||
stroke: $node-selected-color;
|
||||
}
|
||||
g.link_unknown path.link_line {
|
||||
stroke: $link-unknown-color;
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
1046
editor/sass/forms.scss
Normal file
266
editor/sass/header.scss
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
$activeButton: #121212;
|
||||
|
||||
$deployButton: #8C101C;
|
||||
$deployButtonHover: #6E0A1E;
|
||||
$deployButtonActive: #4C0A17;
|
||||
|
||||
$deployDisabledButton: #444;
|
||||
$deployDisabledButtonHover: #555;
|
||||
$deployDisabledButtonActive: #444;
|
||||
|
||||
$headerMenuBackground: #121212;
|
||||
$headerMenuItemHover: #323232;
|
||||
$headerMenuItemDivider: #464646;
|
||||
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: #000;
|
||||
box-sizing: border-box;
|
||||
padding: 0px 0px 0px 20px;
|
||||
color: #C7C7C7;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span.logo {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.header-toolbar {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
float: right;
|
||||
|
||||
> li {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
#header .button {
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
padding: 0px 12px;
|
||||
text-decoration: none;
|
||||
color: #C7C7C7;
|
||||
margin: auto 5px;
|
||||
vertical-align: middle;
|
||||
border-left: 2px solid #000;
|
||||
border-right: 2px solid #000;
|
||||
|
||||
&:hover {
|
||||
border-color: $headerMenuItemHover;
|
||||
}
|
||||
}
|
||||
|
||||
#header .button-group {
|
||||
display: inline-block;
|
||||
margin: auto 15px;
|
||||
vertical-align: middle;
|
||||
clear: both;
|
||||
}
|
||||
#header .button-group > a {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
padding: 4px 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.deploy-button {
|
||||
background: $deployButton;
|
||||
color: #eee !important;
|
||||
|
||||
&:hover {
|
||||
background: $deployButtonHover;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $deployButtonActive;
|
||||
color: #ccc !important;
|
||||
}
|
||||
}
|
||||
|
||||
#btn-deploy {
|
||||
|
||||
padding: 4px 12px;
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
background: $deployDisabledButton;
|
||||
color: #999 !important;
|
||||
|
||||
img {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&+ #btn-deploy-options {
|
||||
background: $deployDisabledButton;
|
||||
color: #ddd;
|
||||
}
|
||||
&+ #btn-deploy-options:hover {
|
||||
background: $deployDisabledButtonHover;
|
||||
}
|
||||
&+ #btn-deploy-options:active {
|
||||
background: $deployDisabledButton;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.deploy-button-group.open {
|
||||
#btn-deploy-options {
|
||||
background: $activeButton !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#header .button {
|
||||
&:active, &.active {
|
||||
background: $activeButton;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
#header li.open .button {
|
||||
background: $activeButton;
|
||||
border-color: $activeButton;
|
||||
}
|
||||
|
||||
|
||||
#header ul.dropdown-menu {
|
||||
background: $headerMenuBackground;
|
||||
width: 250px !important;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu li a {
|
||||
color: #C7C7C7;
|
||||
padding: 3px 40px;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu li a img {
|
||||
margin-right: 10px;
|
||||
padding: 4px;
|
||||
border: 3px solid rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu li a.active img {
|
||||
border: 3px solid #777677;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu li a span.menu-label-container {
|
||||
width: 180px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
text-indent: 0px;
|
||||
}
|
||||
#header ul.dropdown-menu li a span.menu-label {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
text-indent: 0px;
|
||||
}
|
||||
#header ul.dropdown-menu li a span.menu-sublabel {
|
||||
color: #aeaeae;
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
text-indent: 0px;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu > li:hover > a,
|
||||
#header ul.dropdown-menu > li:focus > a {
|
||||
background: $headerMenuItemHover !important;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu li.divider {
|
||||
background: $headerMenuItemDivider;
|
||||
border-bottom-color: $headerMenuItemHover;
|
||||
}
|
||||
#header ul.dropdown-menu li.disabled a {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#header ul.dropdown-menu > li.disabled:hover > a,
|
||||
#header ul.dropdown-menu > li.disabled:focus > a {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Deploy menu customisations */
|
||||
#header ul#btn-deploy-options-submenu {
|
||||
width: 300px !important;
|
||||
}
|
||||
#header ul#btn-deploy-options-submenu li a span.menu-label {
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
text-indent: 0px;
|
||||
}
|
||||
#header ul#btn-deploy-options-submenu li a {
|
||||
padding: 10px 30px;
|
||||
color: #fff;
|
||||
}
|
||||
#header ul#btn-deploy-options-submenu li a > i.fa {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* User menu customisations */
|
||||
#header ul#btn-usermenu-submenu li a#btn-username > .menu-label {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
98
editor/sass/jquery.scss
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.ui-widget {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
.ui-widget input {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* jQuery Theme overrides */
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.ui-autocomplete {
|
||||
max-height: 250px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.ui-dialog {
|
||||
border-radius: 1px;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
@include component-shadow;
|
||||
}
|
||||
.ui-dialog .ui-dialog-content {
|
||||
padding: 25px 25px 10px 25px;
|
||||
}
|
||||
.ui-dialog .ui-dialog-title {
|
||||
width: auto;
|
||||
}
|
||||
.ui-dialog .ui-dialog-titlebar {
|
||||
padding: 10px;
|
||||
background: #f3f3f3;
|
||||
border: none;
|
||||
border-bottom: 1px solid #999;
|
||||
border-radius: 0;
|
||||
}
|
||||
.ui-corner-all {
|
||||
border-radius: 1px;
|
||||
}
|
||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
|
||||
background: #f3f3f3;
|
||||
}
|
||||
.ui-dialog-no-close .ui-dialog-titlebar-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-dialog-buttonset {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
|
||||
float: none;
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane {
|
||||
padding: .3em 1em .5em 1em;
|
||||
}
|
||||
|
||||
.ui-spinner {
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
border: 1px solid $form-input-border-color;
|
||||
}
|
||||
.ui-spinner input {
|
||||
margin: 0 17px 0 0;
|
||||
padding: 6px;
|
||||
border: none;
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|