Compare commits
946 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
63191bc641 | ||
|
9f012c261a | ||
|
dc7701ad70 | ||
|
e8666827e6 | ||
|
5e2c51a741 | ||
|
51421ce657 | ||
|
339e6039e1 | ||
|
43054906dc | ||
|
57dedcf816 | ||
|
4dc21c43fa | ||
|
1c86908b90 | ||
|
9b26973883 | ||
|
2585e983a5 | ||
|
a808cb44c2 | ||
|
edd9d2cb9c | ||
|
33d24f79ce | ||
|
cc095e4edf | ||
|
07641d57ab | ||
|
5643c51507 | ||
|
ad6254c0b8 | ||
|
d6ca421d59 | ||
|
79bd01f810 | ||
|
e357352240 | ||
|
0accfade02 | ||
|
00b7afe3ae | ||
|
2e76541fa5 | ||
|
e2911078e3 | ||
|
c6157687c9 | ||
|
87a1818486 | ||
|
d6c5d91e2c | ||
|
e042d9e8cd | ||
|
07c8c4bb45 | ||
|
2dd572f5bd | ||
|
d4a27f88a1 | ||
|
1513dbfcdd | ||
|
8451c29a25 | ||
|
ae7f1b38a8 | ||
|
83dad88ad3 | ||
|
8eb1a02407 | ||
|
1e6f45852f | ||
|
3849855b57 | ||
|
f2712f296b | ||
|
c7f13e3d0c | ||
|
d1fe997bf7 | ||
|
86c8a5de81 | ||
|
010abbd3d5 | ||
|
3123aa6279 | ||
|
dedda19626 | ||
|
c31ffb98b0 | ||
|
3ef6f29d6e | ||
|
26c42e500f | ||
|
53a515176b | ||
|
fbf7ee50eb | ||
|
b2aae93fa6 | ||
|
a494954275 | ||
|
f5d7903ecb | ||
|
9bbe0799bd | ||
|
f3eb85c449 | ||
|
74e1ef0823 | ||
|
982997c3df | ||
|
66005a2688 | ||
|
42cd6f94a7 | ||
|
dfc20f3cc9 | ||
|
2b5a1ce6d4 | ||
|
28823802ea | ||
|
2128b57ab2 | ||
|
c8ccacb035 | ||
|
3334c795e0 | ||
|
b97d251787 | ||
|
482c4e9c5e | ||
|
1712146836 | ||
|
c8d2d690f0 | ||
|
f2d4648384 | ||
|
86ca75bcd5 | ||
|
31aa3901cc | ||
|
ab831c34f3 | ||
|
8443e48240 | ||
|
a08b29dbd1 | ||
|
97621b41b9 | ||
|
f462446213 | ||
|
6b96c1876a | ||
|
0aaea1ec40 | ||
|
7d6ce1ec12 | ||
|
b052324d36 | ||
|
a22f819f40 | ||
|
07acc6642f | ||
|
950611ed31 | ||
|
84e6417877 | ||
|
a87548a991 | ||
|
5e5a220f68 | ||
|
f451d0644a | ||
|
e9f0877da8 | ||
|
f6c6301733 | ||
|
0709a118e3 | ||
|
7576878ba5 | ||
|
083e253e45 | ||
|
da67e69544 | ||
|
0ed8d28342 | ||
|
27f9056360 | ||
|
8c075bfde3 | ||
|
cae755d948 | ||
|
ca9d84b1b7 | ||
|
f983e4da9f | ||
|
109270b437 | ||
|
0bfbb12211 | ||
|
69998243cc | ||
|
8b61c121e6 | ||
|
56ef982345 | ||
|
8389df9729 | ||
|
a8f1a6df2c | ||
|
68e51bb886 | ||
|
462c259f3a | ||
|
f6f4b0784b | ||
|
57b39022d0 | ||
|
110f0bf169 | ||
|
3f0fcb70a4 | ||
|
d8c7ea8144 | ||
|
44db2c28a3 | ||
|
8d5f99640e | ||
|
ffe417976c | ||
|
d04ac00732 | ||
|
dd5e851339 | ||
|
2890575b3d | ||
|
8b3fbae3f6 | ||
|
c97ab18e62 | ||
|
b0ffc12142 | ||
|
687a66344e | ||
|
a5afc258b1 | ||
|
afb5e8cbce | ||
|
83b40a7ba6 | ||
|
cf1371bfdf | ||
|
89fff339d5 | ||
|
e11abd2508 | ||
|
9a0177b900 | ||
|
5510dffe18 | ||
|
7909ca24d3 | ||
|
eee2996e8a | ||
|
2577631334 | ||
|
9f91493cd1 | ||
|
fcec704b7b | ||
|
2066d53d3f | ||
|
8822335700 | ||
|
f461b121e1 | ||
|
92b393d3df | ||
|
4fb2a44d74 | ||
|
00429ebe70 | ||
|
25537e01d4 | ||
|
65e4d83625 | ||
|
ed6272ce12 | ||
|
9e4187d6a8 | ||
|
b4dc66944a | ||
|
a51056a91f | ||
|
a3692944a2 | ||
|
03765afefa | ||
|
66213d151d | ||
|
0c699ae57f | ||
|
bf8d549cf7 | ||
|
1261bf97ea | ||
|
41552625e0 | ||
|
27ef7d972f | ||
|
6fc3aab907 | ||
|
10681f97d9 | ||
|
475d9e110e | ||
|
8c5fab61e6 | ||
|
e7ccff5a4b | ||
|
81e08e06e4 | ||
|
b006ccf610 | ||
|
bb0e48f271 | ||
|
6d0dffcdf7 | ||
|
14b84f0c7b | ||
|
8b6e287a74 | ||
|
1a5751ff1d | ||
|
851048077c | ||
|
5f74a1d237 | ||
|
e9b1e287ba | ||
|
da7b3ce9e4 | ||
|
35b3912808 | ||
|
e28f933f64 | ||
|
57bc83b2a7 | ||
|
ec43fc4fe2 | ||
|
8f2a0b63d9 | ||
|
bb6e27f662 | ||
|
4e28a308b0 | ||
|
04ffaeb2b8 | ||
|
d7f249eac4 | ||
|
a5064b3ab6 | ||
|
dd5821ee1b | ||
|
4c9d53388c | ||
|
70f101497d | ||
|
56cb985de9 | ||
|
d614b7c39f | ||
|
0ff65f6805 | ||
|
591b5f3f91 | ||
|
50fddf474b | ||
|
3a78a2fedd | ||
|
9552055b08 | ||
|
71bd5cd9e9 | ||
|
7cff7ed297 | ||
|
6ba0d83778 | ||
|
ca2ef7e71f | ||
|
977a9e1c83 | ||
|
7da108e129 | ||
|
7b14e753cd | ||
|
9863b6e178 | ||
|
0789b82c15 | ||
|
a477c0b827 | ||
|
3cb423a0b4 | ||
|
9c8d9550a7 | ||
|
8d16f3c8be | ||
|
b4c92b457a | ||
|
426fcc2fdd | ||
|
5cb9a5b7eb | ||
|
64a6fe11da | ||
|
f72f7cdaa7 | ||
|
01f0d5390f | ||
|
ed9951f065 | ||
|
4249cf5d69 | ||
|
5da45b404c | ||
|
8b7e367416 | ||
|
28da2dc38a | ||
|
5c5de028da | ||
|
b861f490c6 | ||
|
e7dccf04d2 | ||
|
3e235ecc0b | ||
|
deeaa09360 | ||
|
2e7a97fb88 | ||
|
9c92eeb9f5 | ||
|
3e24601518 | ||
|
266a644ca6 | ||
|
def93214de | ||
|
a520240b25 | ||
|
e7eb02fcb7 | ||
|
72f9471f2b | ||
|
67449eb65a | ||
|
069a47f35a | ||
|
53b07581cf | ||
|
11a29b4633 | ||
|
bdb46cbbe4 | ||
|
273acc0ec4 | ||
|
1153619a03 | ||
|
f89ddb5f7a | ||
|
4494c11b03 | ||
|
694649e8f9 | ||
|
9f925140c9 | ||
|
6a37a823df | ||
|
f0e9a0279f | ||
|
863b85714d | ||
|
dfc79e3122 | ||
|
afde3d0ab8 | ||
|
7419f62a20 | ||
|
f06b52625f | ||
|
ccfbd69f24 | ||
|
d859412785 | ||
|
3840bd117c | ||
|
aff8a7802a | ||
|
6169e4299a | ||
|
d9648ca76b | ||
|
348b642d25 | ||
|
a1830def8e | ||
|
98b875c4a0 | ||
|
d242f67be3 | ||
|
ce6513e7f7 | ||
|
bd75c1c753 | ||
|
48d3b8f37a | ||
|
4c573b208c | ||
|
a5228875a6 | ||
|
c51866c2c5 | ||
|
bbaf7bf247 | ||
|
04673c65f4 | ||
|
57ae297efd | ||
|
0bb78ae491 | ||
|
d9363f4974 | ||
|
b54e9edfa6 | ||
|
cf81de415a | ||
|
4cd78692e2 | ||
|
5fd1ff5137 | ||
|
fe95a98339 | ||
|
da73972c41 | ||
|
014389c55f | ||
|
ff4edccbbc | ||
|
1e7ce2cfe7 | ||
|
bc8e459ae6 | ||
|
53a9a5fe93 | ||
|
17e4bf1a11 | ||
|
bec4e429f9 | ||
|
472fdc65a9 | ||
|
28a4ba1aad | ||
|
dd9fc6a250 | ||
|
7802939bb0 | ||
|
d4a21be666 | ||
|
0d9abbb8b6 | ||
|
9a32e79603 | ||
|
206b8ac34a | ||
|
da1321f1de | ||
|
748c7aedee | ||
|
483c4352d3 | ||
|
1afd4e7acc | ||
|
21e349c22a | ||
|
da4446c20f | ||
|
986ce8163f | ||
|
97e5c2e571 | ||
|
2a753c9d22 | ||
|
f96b40cff2 | ||
|
e0927d190f | ||
|
0c636ca707 | ||
|
d49ed69a0a | ||
|
4b73a92f71 | ||
|
e95d4a9010 | ||
|
d950ceceea | ||
|
43f55c6038 | ||
|
489c552dbe | ||
|
c223e7f58f | ||
|
86d4179039 | ||
|
b0e2b3d525 | ||
|
ea6313c9a1 | ||
|
c631b393e9 | ||
|
8df9eee6d2 | ||
|
322a6ebed3 | ||
|
669f3d9ba1 | ||
|
059ec3b50c | ||
|
4f496c37be | ||
|
d3956f9816 | ||
|
a5265784e8 | ||
|
dbaa1ed59c | ||
|
8742bf354b | ||
|
9152daa13b | ||
|
6305c5b55f | ||
|
723a3e628e | ||
|
af0b740fbd | ||
|
31255bd66b | ||
|
1f95071a37 | ||
|
5f77531a39 | ||
|
23b5ac4582 | ||
|
4f2e4b58e4 | ||
|
abd3d752f5 | ||
|
8d54126127 | ||
|
55c830b812 | ||
|
e48cbafbd6 | ||
|
9bfc6d376b | ||
|
c98b9dfaa3 | ||
|
ea89aff3db | ||
|
e2be5c6383 | ||
|
f939d52551 | ||
|
ed1da5cf7b | ||
|
a718f34c58 | ||
|
da61fe12d0 | ||
|
00cb8d5bce | ||
|
ebb0d1a46d | ||
|
f04e42e4da | ||
|
22329ca106 | ||
|
c4932e3cf9 | ||
|
bc8acd24ae | ||
|
a0ac79384d | ||
|
7e2dbb13e4 | ||
|
429a87f88a | ||
|
c0fcc20f23 | ||
|
a3497a5fc7 | ||
|
54a2923c65 | ||
|
400f51d921 | ||
|
50f1a17920 | ||
|
72dcb53d23 | ||
|
0111ab8901 | ||
|
fd04b5851b | ||
|
94b196bfcf | ||
|
5d9c16ffbf | ||
|
7b63680be1 | ||
|
d30f1e639b | ||
|
27149fe10f | ||
|
3880bd7ca4 | ||
|
7ec7354755 | ||
|
afa201790d | ||
|
cba4791b65 | ||
|
3ec07a9161 | ||
|
6368a43a02 | ||
|
39d0419b65 | ||
|
ab4d3c025e | ||
|
19c3a7f36c | ||
|
22b32b1684 | ||
|
1a5a548fbf | ||
|
c03db99a21 | ||
|
141c8a1faf | ||
|
3e3b388ca7 | ||
|
4706b814dd | ||
|
b3fa866385 | ||
|
e3f72a697e | ||
|
148fbefbbb | ||
|
3f054bdf7e | ||
|
995268c5df | ||
|
a03861cb6d | ||
|
426453d925 | ||
|
ef9255a87c | ||
|
5872541b1b | ||
|
6076be011a | ||
|
67d5bc2399 | ||
|
c91a3f3f78 | ||
|
083dc213f0 | ||
|
56768fbf2c | ||
|
293725afcd | ||
|
e90054c93c | ||
|
553935ecc8 | ||
|
06542d95f2 | ||
|
5bf9819bd1 | ||
|
fc679adefb | ||
|
e136080888 | ||
|
a05c0f4bb2 | ||
|
fa8d31609e | ||
|
7da7887ed2 | ||
|
5a81ce8569 | ||
|
0a009e2a15 | ||
|
8e78df09da | ||
|
16048112c6 | ||
|
5c39e330cb | ||
|
0138eb3f5f | ||
|
4f36435628 | ||
|
9876570189 | ||
|
961c9f3fa9 | ||
|
27e34f20fb | ||
|
eb1e967be6 | ||
|
06e48e9cb1 | ||
|
8c8c9b13b9 | ||
|
42c3ee430f | ||
|
dfc427c192 | ||
|
c3926f6513 | ||
|
64c95f1d1b | ||
|
fae8b35961 | ||
|
0a3c889cc6 | ||
|
98338508d2 | ||
|
44ec551ed9 | ||
|
e256110eac | ||
|
8849f11eb5 | ||
|
6f888068d2 | ||
|
a9e72858df | ||
|
e09ac859d3 | ||
|
e1ba2f7254 | ||
|
7abae51b42 | ||
|
8febfbe329 | ||
|
bac288fa52 | ||
|
691ef12e04 | ||
|
96a0a9d2d2 | ||
|
2dac28a421 | ||
|
025e4ab6d1 | ||
|
2d92b44f52 | ||
|
6b04d512ae | ||
|
df751be331 | ||
|
c38aa91a4d | ||
|
a74009caa9 | ||
|
40c87ab14c | ||
|
ecbf4add6b | ||
|
a8ade083d4 | ||
|
f1d2b7ffa0 | ||
|
ec5aaa5bfb | ||
|
7a9696526a | ||
|
7176f3ee2b | ||
|
47b4ebc92f | ||
|
44a51e849d | ||
|
1e33843798 | ||
|
3823150e46 | ||
|
7042d87444 | ||
|
4d6846047f | ||
|
8ddfa9eb29 | ||
|
c0842455b9 | ||
|
e8c4caaf74 | ||
|
76e8512869 | ||
|
907ce5c079 | ||
|
3c31ef4643 | ||
|
9734c755ae | ||
|
89825dae04 | ||
|
ce4d44ea48 | ||
|
105b25287f | ||
|
b3c3bffeaa | ||
|
972e6fc6b3 | ||
|
fb2f307a26 | ||
|
58c2f5dd3d | ||
|
43ad8706aa | ||
|
fde77cec5d | ||
|
15494dda84 | ||
|
c6a98da256 | ||
|
2e2b5ad13e | ||
|
ea22ffa2bf | ||
|
fadd2167f5 | ||
|
82fb15896c | ||
|
960d15491d | ||
|
495dd3f2e0 | ||
|
8c0c843367 | ||
|
4ac4995762 | ||
|
118c50ce8e | ||
|
38c541361e | ||
|
8eb8ac0f88 | ||
|
f7f58a2347 | ||
|
1697aee9f6 | ||
|
3ee98e730d | ||
|
d6c5f9b57f | ||
|
882b593ba4 | ||
|
79e9641c09 | ||
|
a869642705 | ||
|
8ee5be7031 | ||
|
8e8e13a3a2 | ||
|
6c464ed3a4 | ||
|
716d0c1135 | ||
|
989f3459d5 | ||
|
b8f40d4e39 | ||
|
401afcbadd | ||
|
a170623b4c | ||
|
d9544e4ac5 | ||
|
530a1fce81 | ||
|
b7428ab627 | ||
|
5a714242a4 | ||
|
bc8ea998bb | ||
|
e179753853 | ||
|
80676da300 | ||
|
f4ffdce3d0 | ||
|
32d14ddbcf | ||
|
3e76e73839 | ||
|
9c0b65a4c2 | ||
|
2f882913f0 | ||
|
e407fc857e | ||
|
30a94bdaf5 | ||
|
195f581da7 | ||
|
a8c491bf2b | ||
|
8e30910065 | ||
|
47f7cb52d8 | ||
|
14c36687e0 | ||
|
a3cb0e996d | ||
|
bbdfa03687 | ||
|
d7b3e86096 | ||
|
1ce4765ed8 | ||
|
a9ee417c1f | ||
|
53d9eab94e | ||
|
96ea81894d | ||
|
b1f14031a9 | ||
|
575d166947 | ||
|
796810f460 | ||
|
74ae2eca3e | ||
|
ac1e750f14 | ||
|
a396bca06a | ||
|
deff93f95e | ||
|
78edf89e52 | ||
|
00a9542edf | ||
|
31d72b2193 | ||
|
d09c992ecc | ||
|
e07a523c3b | ||
|
3d31a0abca | ||
|
132dcdeb43 | ||
|
50b28842a4 | ||
|
536a0c9142 | ||
|
76a6730ef4 | ||
|
0c8b54253a | ||
|
1f09c29aeb | ||
|
d58788f721 | ||
|
e299baf1a0 | ||
|
af1bcf33c1 | ||
|
7abda9bc52 | ||
|
2c1da2d546 | ||
|
8506fd0c4b | ||
|
6c093eef99 | ||
|
12a06cacce | ||
|
b50b23318b | ||
|
166a798c09 | ||
|
4096589d21 | ||
|
2e86a41944 | ||
|
6bc4b235bb | ||
|
38390c6285 | ||
|
e96fff573d | ||
|
16a5aa7368 | ||
|
f331d906b4 | ||
|
d7445a5bb9 | ||
|
6c4611a934 | ||
|
5b5b7d2be1 | ||
|
55679694c9 | ||
|
c079576e49 | ||
|
5179cc77f5 | ||
|
406581d522 | ||
|
9a60294813 | ||
|
82036dd84a | ||
|
b29062a931 | ||
|
14a1899c23 | ||
|
d51eadb6c2 | ||
|
bead24e760 | ||
|
0b308deb79 | ||
|
d1300c0632 | ||
|
014fca2d61 | ||
|
fea6280bff | ||
|
7687cf6661 | ||
|
63ebee22dd | ||
|
4f8ddad935 | ||
|
bd70c4b6e7 | ||
|
1414308179 | ||
|
fd6682cfce | ||
|
9d481858a0 | ||
|
4302deb5a6 | ||
|
d67a54a66a | ||
|
b604db83f6 | ||
|
760dd022dc | ||
|
c379f1b197 | ||
|
4770a06679 | ||
|
252135532d | ||
|
aaab0d0d0b | ||
|
aa35484a30 | ||
|
a6a0352b70 | ||
|
f80c41058c | ||
|
84a0e8ceff | ||
|
8b6c14b05d | ||
|
cb854c40ba | ||
|
bf4369a01e | ||
|
8f3de41049 | ||
|
fea68d4eda | ||
|
2778c38b55 | ||
|
67bd4f373f | ||
|
2b423fe9be | ||
|
3bc4e7125b | ||
|
8cedf76b70 | ||
|
79853d8583 | ||
|
17a83436b3 | ||
|
5073cf8dd7 | ||
|
77f6652fa9 | ||
|
3756e41522 | ||
|
ac889e18d2 | ||
|
9650b3ed38 | ||
|
b7a83383c9 | ||
|
d4919e27bd | ||
|
4f6e76cace | ||
|
4d97d9d300 | ||
|
1419a79933 | ||
|
cfd590a397 | ||
|
d67074f4b7 | ||
|
4a514b9060 | ||
|
8e32427109 | ||
|
fe9ff0a297 | ||
|
7281d273a1 | ||
|
d4548deeb3 | ||
|
debd5c4496 | ||
|
62496d80d5 | ||
|
bc53d302ff | ||
|
1a1c6a73b6 | ||
|
9158766297 | ||
|
b84aba5c98 | ||
|
66459f1bd6 | ||
|
74335990e3 | ||
|
d371511d1d | ||
|
0a7bd848c6 | ||
|
de352dcdc2 | ||
|
242fe1ef86 | ||
|
3db84d5bf1 | ||
|
4d031891e5 | ||
|
a7e7254317 | ||
|
8e24a958b7 | ||
|
bd80cf4f83 | ||
|
abf5944316 | ||
|
85a8e7283f | ||
|
ac7448759b | ||
|
c10c687653 | ||
|
2c5d5148b8 | ||
|
6fb9739245 | ||
|
9bc9994354 | ||
|
35965e55b5 | ||
|
4532cadb14 | ||
|
6e956ef03a | ||
|
4afda2f53b | ||
|
9db1166ce0 | ||
|
3de2f3d1da | ||
|
9f3233175b | ||
|
649c82f7d7 | ||
|
c18119f26e | ||
|
ef6a0e5947 | ||
|
045f658ef9 | ||
|
c7f0f9639a | ||
|
52779ac518 | ||
|
790ad8eb68 | ||
|
e43b342048 | ||
|
5959da2d37 | ||
|
e15de8cf37 | ||
|
13da5e7327 | ||
|
3bbf2fa737 | ||
|
cfe7c372d3 | ||
|
3fcc1b5680 | ||
|
6d54050b4f |
17
.gitignore
vendored
@@ -1,7 +1,16 @@
|
||||
node_modules
|
||||
.config.json
|
||||
.dist
|
||||
.jshintignore
|
||||
.npm
|
||||
.project
|
||||
.sessions.json
|
||||
.settings
|
||||
.tern-project
|
||||
*.backup
|
||||
*_cred*
|
||||
coverage
|
||||
credentials.json
|
||||
flows*.json
|
||||
flows.backup
|
||||
*_cred*
|
||||
nodes/node-red-nodes/
|
||||
.npm
|
||||
node_modules
|
||||
public
|
||||
|
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,6 @@
|
||||
.git/*
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
*.json
|
||||
lib/*
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
||||
|
7
.npmignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.settings
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.project
|
||||
.tern-project
|
||||
.travis.yml
|
||||
.git
|
@@ -1,5 +1,10 @@
|
||||
language: node_js
|
||||
before_install:
|
||||
- npm install -g npm@~1.4.18
|
||||
node_js:
|
||||
- "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:
|
||||
- npm install -g istanbul
|
||||
- npm install coveralls
|
||||
|
@@ -2,10 +2,14 @@
|
||||
|
||||
We welcome contributions, but request you follow these guidelines.
|
||||
|
||||
- [Raising issues](#raising-issues)
|
||||
- [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
|
||||
@@ -20,50 +24,22 @@ 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?
|
||||
|
||||
|
||||
## New features
|
||||
## Feature requests
|
||||
|
||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
|
||||
## Pull-Requests
|
||||
|
||||
### Changes to existing code
|
||||
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
|
||||
If you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
|
||||
|
||||
### New nodes
|
||||
|
||||
The plugin nature of Node-RED means anyone can create a new node to extend
|
||||
its capabilities.
|
||||
|
||||
We want to avoid duplication as that can lead to confusion. Many of our existing
|
||||
nodes offer a starting point of functionality. If they are missing features,
|
||||
we would rather extend them than add separate 'advanced' versions. But the key
|
||||
to that approach is getting the UX right to not lose the simplicity.
|
||||
|
||||
To contribute a new node, please raise a pull-request against the
|
||||
`node-red-nodes` repository.
|
||||
|
||||
Eventually, the nodes will be npm-installable, but we're not there yet. We'll
|
||||
also have some sort of registry of nodes to help with discoverability.
|
||||
|
||||
### Coding standards
|
||||
|
||||
Please ensure you follow the coding standards used through-out the existing
|
||||
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.
|
||||
|
||||
### Contributor License Aggreement
|
||||
### 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:
|
||||
@@ -74,9 +50,12 @@ You can download the CLAs here:
|
||||
If you are an IBMer, please contact us directly as the contribution process is
|
||||
slightly different.
|
||||
|
||||
### Coding standards
|
||||
|
||||
Please ensure you follow the coding standards used through-out the existing
|
||||
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` and so on, closing brace
|
||||
on its own line.
|
||||
|
386
Gruntfile.js
@@ -1,24 +1,378 @@
|
||||
/**
|
||||
* 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 path = require("path");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'tap'
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
dist: ".dist"
|
||||
},
|
||||
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"]}
|
||||
},
|
||||
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']
|
||||
},
|
||||
all: { 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/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/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"
|
||||
],
|
||||
dest: "public/red/red.js"
|
||||
},
|
||||
vendor: {
|
||||
files: {
|
||||
"public/vendor/vendor.js": [
|
||||
"editor/vendor/jquery/js/jquery-1.11.1.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"
|
||||
],
|
||||
"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'
|
||||
}]
|
||||
}
|
||||
},
|
||||
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']
|
||||
}
|
||||
},
|
||||
|
||||
nodemon: {
|
||||
/* uses .nodemonignore */
|
||||
dev: {
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args:['-v'],
|
||||
ext: 'js,html',
|
||||
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: [{
|
||||
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 %>')
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
compress: {
|
||||
release: {
|
||||
options: {
|
||||
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
|
||||
},
|
||||
expand: true,
|
||||
cwd: '<%= paths.dist %>/',
|
||||
src: ['node-red-<%= pkg.version %>/**']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load the plugin that provides the "uglify" task.
|
||||
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
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');
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('default', ['simplemocha']);
|
||||
|
||||
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','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','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
|
||||
|
56
README.md
@@ -1,38 +1,59 @@
|
||||
# Node-RED
|
||||
|
||||
http://nodered.org
|
||||
http://nodered.org
|
||||
|
||||
[](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>
|
||||
|
||||
## Documentation
|
||||
## Getting Help
|
||||
|
||||
More documentation can be found [here](http://nodered.org/docs).
|
||||
|
||||
For further help, or general discussion, there is also a [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 IE9.
|
||||
If you want to run the latest code from git, here's how to get started:
|
||||
|
||||
We do not yet support mobile browsers, although that is high on our priority
|
||||
list.
|
||||
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
|
||||
|
||||
Please see 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
|
||||
|
||||
@@ -45,4 +66,5 @@ For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013, 2014 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
Copyright 2013, 2015 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
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 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 |
BIN
editor/icons/mouse.png
Normal file
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 |
BIN
editor/icons/subflow.png
Normal file
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 |
BIN
editor/images/deploy-flows-o.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
editor/images/deploy-flows.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/images/deploy-full-o.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
editor/images/deploy-full.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
editor/images/deploy-nodes-o.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
editor/images/deploy-nodes.png
Normal file
After Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
BIN
editor/images/node-red-256.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
editor/images/node-red.png
Normal file
After Width: | Height: | Size: 1019 B |
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
177
editor/index.html
Normal file
@@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<head>
|
||||
<title>Node-RED</title>
|
||||
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="vendor/jquery/css/smoothness/jquery-ui-1.10.3.custom.min.css" rel="stylesheet" media="screen">
|
||||
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="vendor/vendor.css">
|
||||
<link rel="stylesheet" href="red/style.min.css">
|
||||
</head>
|
||||
<body spellcheck="false">
|
||||
<div id="header">
|
||||
<span class="logo"><img src="red/images/node-red.png"> <span>Node-RED</span></span>
|
||||
<ul class="header-toolbar hide">
|
||||
<li><a id="btn-sidemenu" class="button" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i></a></li>
|
||||
<ul>
|
||||
</div>
|
||||
<div id="main-container" class="sidebar-closed hide">
|
||||
<div id="palette">
|
||||
<img src="red/images/spin.svg" class="palette-spinner hide"/>
|
||||
<div id="palette-container" class="palette-scroll">
|
||||
</div>
|
||||
<div id="palette-search">
|
||||
<i class="fa fa-search"></i><input id="palette-search-input" type="text" placeholder="filter"><a href="#" id="palette-search-clear"><i class="fa fa-times"></i></a></input>
|
||||
</div>
|
||||
</div><!-- /palette -->
|
||||
|
||||
<div id="workspace">
|
||||
<ul id="workspace-tabs"></ul>
|
||||
<div id="workspace-add-tab"><a id="btn-workspace-add-tab" href="#"><i class="fa fa-plus"></i></a></div>
|
||||
<div id="chart"></div>
|
||||
<div id="workspace-toolbar">
|
||||
<a class="button" id="workspace-subflow-edit" href="#"><i class="fa fa-pencil"></i> edit name</a>
|
||||
<a class="button disabled" id="workspace-subflow-add-input" href="#"><i class="fa fa-plus"></i> input</a>
|
||||
<a class="button" id="workspace-subflow-add-output" href="#"><i class="fa fa-plus"></i> output</a>
|
||||
<a class="button" id="workspace-subflow-delete" href="#"><i class="fa fa-trash"></i> delete subflow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chart-zoom-controls">
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-mini" id="btn-zoom-out" href="#"><i class="fa fa-search-minus"></i></a>
|
||||
<a class="btn btn-mini" id="btn-zoom-zero" href="#"><i class="fa fa-dot-circle-o"></i></a>
|
||||
<a class="btn btn-mini" id="btn-zoom-in" href="#"><i class="fa fa-search-plus"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<ul id="sidebar-tabs"></ul>
|
||||
<div id="sidebar-content"></div>
|
||||
</div>
|
||||
|
||||
<div id="sidebar-separator"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="notifications"></div>
|
||||
<div id="dropTarget"><div>Drop the flow here<br/><i class="fa fa-download"></i></div></div>
|
||||
|
||||
<div id="dialog" class="hide"><form id="dialog-form" class="form-horizontal"></form></div>
|
||||
<div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"></form><div class="form-tips" id="node-config-dialog-user-count"></div></div>
|
||||
<div id="subflow-dialog" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-row">
|
||||
<label>Name</label><input type="text" id="subflow-input-name">
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-tips" id="subflow-dialog-user-count"></div>
|
||||
</div>
|
||||
|
||||
<div id="node-dialog-confirm-deploy" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div id="node-dialog-confirm-deploy-config" style="text-align: center; padding-top: 30px;">
|
||||
Some of the nodes are not properly configured. Are you sure you want to deploy?
|
||||
</div>
|
||||
<div id="node-dialog-confirm-deploy-unknown" style="text-align: center; padding-top: 10px;">
|
||||
The workspace contains some unknown node types:
|
||||
<ul style="width: 300px; margin: auto; text-align: left;" id="node-dialog-confirm-deploy-unknown-list"></ul>
|
||||
Are you sure you want to deploy?
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="node-dialog-library-save-confirm" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div style="text-align: center; padding-top: 30px;">
|
||||
A <span id="node-dialog-library-save-type"></span> called <span id="node-dialog-library-save-name"></span> already exists. Overwrite?
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="node-dialog-library-save" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-row">
|
||||
<label for="node-dialog-library-save-folder"><i class="fa fa-folder-open"></i> Folder</label>
|
||||
<input type="text" id="node-dialog-library-save-folder" placeholder="Folder">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-dialog-library-save-filename"><i class="fa fa-file"></i> Filename</label>
|
||||
<input type="text" id="node-dialog-library-save-filename" placeholder="Filename">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="node-dialog-library-lookup" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-row">
|
||||
<ul id="node-dialog-library-breadcrumbs" class="breadcrumb">
|
||||
<li class="active"><a href="#">Library</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="vertical-align: top; display: inline-block; height: 100%; width: 30%; padding-right: 20px;">
|
||||
<div id="node-select-library" style="border: 1px solid #999; width: 100%; height: 100%; overflow:scroll;"><ul></ul></div>
|
||||
</div>
|
||||
<div style="vertical-align: top; display: inline-block;width: 65%; height: 100%;">
|
||||
<div style="height: 100%; width: 95%;" class="node-text-editor" id="node-select-library-text" ></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="node-dialog-rename-workspace" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-row">
|
||||
<label for="node-input-workspace-name" ><i class="fa fa-tag"></i> Name:</label>
|
||||
<input type="text" id="node-input-workspace-name">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="node-dialog-delete-workspace" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div style="text-align: center; padding-top: 30px;">
|
||||
Are you sure you want to delete '<span id="node-dialog-delete-workspace-name"></span>'?
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/x-red" data-template-name="export-library-dialog">
|
||||
<div class="form-row">
|
||||
<label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label>
|
||||
<input type="text" id="node-input-filename" placeholder="Filename">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="subflow">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script src="vendor/vendor.js"></script>
|
||||
<script src="vendor/ace/ace.js"></script>
|
||||
<script src="vendor/ace/ext-language_tools.js"></script>
|
||||
<script src="red/red.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
116
editor/js/comms.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright 2014 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.comms = (function() {
|
||||
|
||||
var errornotification = null;
|
||||
var clearErrorTimer = null;
|
||||
|
||||
var subscriptions = {};
|
||||
var ws;
|
||||
var pendingAuth = false;
|
||||
|
||||
function connectWS() {
|
||||
var path = location.hostname+":"+location.port+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)) {
|
||||
ws.send(JSON.stringify({subscribe:t}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
if (errornotification) {
|
||||
clearErrorTimer = setTimeout(function() {
|
||||
errornotification.close();
|
||||
errornotification = null;
|
||||
},1000);
|
||||
}
|
||||
if (pendingAuth) {
|
||||
ws.send(JSON.stringify({auth:auth_tokens.access_token}));
|
||||
} else {
|
||||
completeConnection();
|
||||
}
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var msg = JSON.parse(event.data);
|
||||
if (pendingAuth && msg.auth == "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
} else if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = function() {
|
||||
if (errornotification == null) {
|
||||
errornotification = RED.notify("<b>Error</b>: Lost connection to server","error",true);
|
||||
} else if (clearErrorTimer) {
|
||||
clearTimeout(clearErrorTimer);
|
||||
clearErrorTimer = null;
|
||||
}
|
||||
setTimeout(connectWS,1000);
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe(topic,callback) {
|
||||
if (subscriptions[topic] == null) {
|
||||
subscriptions[topic] = [];
|
||||
}
|
||||
subscriptions[topic].push(callback);
|
||||
if (ws && ws.readyState == 1) {
|
||||
ws.send(JSON.stringify({subscribe:topic}));
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribe(topic,callback) {
|
||||
if (subscriptions[topic]) {
|
||||
for (var i=0;i<subscriptions[topic].length;i++) {
|
||||
if (subscriptions[topic][i] === callback) {
|
||||
subscriptions[topic].splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subscriptions[topic].length === 0) {
|
||||
delete subscriptions[topic];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect: connectWS,
|
||||
subscribe: subscribe,
|
||||
unsubscribe:unsubscribe
|
||||
}
|
||||
})();
|
210
editor/js/history.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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.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() {
|
||||
for (var i=0;i<undo_history.length;i++) {
|
||||
undo_history[i].dirty = true;
|
||||
}
|
||||
},
|
||||
depth: function() {
|
||||
return undo_history.length;
|
||||
},
|
||||
push: function(ev) {
|
||||
undo_history.push(ev);
|
||||
},
|
||||
pop: function() {
|
||||
var ev = undo_history.pop();
|
||||
var i;
|
||||
var node;
|
||||
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]);
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.removeWorkspace(ev.workspaces[i].id);
|
||||
RED.workspaces.remove(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflows) {
|
||||
for (i=0;i<ev.subflows.length;i++) {
|
||||
RED.nodes.removeSubflow(ev.subflows[i]);
|
||||
RED.workspaces.remove(ev.subflows[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.addWorkspace(ev.workspaces[i]);
|
||||
RED.workspaces.add(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow);
|
||||
}
|
||||
var subflow;
|
||||
if (ev.subflowInputs && ev.subflowInputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowInputs[0].z);
|
||||
subflow.in.push(ev.subflowInputs[0]);
|
||||
subflow.in[0].dirty = true;
|
||||
}
|
||||
if (ev.subflowOutputs && ev.subflowOutputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowOutputs[0].z);
|
||||
ev.subflowOutputs.sort(function(a,b) { return a.i-b.i});
|
||||
for (i=0;i<ev.subflowOutputs.length;i++) {
|
||||
var output = ev.subflowOutputs[i];
|
||||
subflow.out.splice(output.i,0,output);
|
||||
for (var j=output.i+1;j<subflow.out.length;j++) {
|
||||
subflow.out[j].i++;
|
||||
subflow.out[j].dirty = true;
|
||||
}
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow:"+subflow.id) {
|
||||
if (l.sourcePort >= output.i) {
|
||||
l.sourcePort++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
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.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.add(ev.nodes[i]);
|
||||
modifiedTabs[ev.nodes[i].z] = true;
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "move") {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
var n = ev.nodes[i];
|
||||
n.n.x = n.ox;
|
||||
n.n.y = n.oy;
|
||||
n.n.dirty = true;
|
||||
}
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.hasOwnProperty('inputCount')) {
|
||||
if (ev.node.in.length > ev.subflow.inputCount) {
|
||||
ev.node.in.splice(ev.subflow.inputCount);
|
||||
} else if (ev.subflow.inputs.length > 0) {
|
||||
ev.node.in = ev.node.in.concat(ev.subflow.inputs);
|
||||
}
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('outputCount')) {
|
||||
if (ev.node.out.length > ev.subflow.outputCount) {
|
||||
ev.node.out.splice(ev.subflow.outputCount);
|
||||
} else if (ev.subflow.outputs.length > 0) {
|
||||
ev.node.out = ev.node.out.concat(ev.subflow.outputs);
|
||||
}
|
||||
}
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.changed = ev.changed;
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
|
||||
RED.palette.refresh();
|
||||
} else {
|
||||
RED.editor.updateNodeProperties(ev.node);
|
||||
RED.editor.validateNode(ev.node);
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
ev.node.dirty = true;
|
||||
ev.node.changed = ev.changed;
|
||||
} else if (ev.t == "createSubflow") {
|
||||
if (ev.nodes) {
|
||||
RED.nodes.filterNodes({z:ev.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]);
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow);
|
||||
RED.workspaces.remove(ev.subflow);
|
||||
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
210
editor/js/main.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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 loadSettings() {
|
||||
RED.settings.init(loadNodeList);
|
||||
}
|
||||
|
||||
function loadNodeList() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
RED.nodes.setNodeList(data);
|
||||
loadNodes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadNodes() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"text/html"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
$("body").append(data);
|
||||
$(".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) {
|
||||
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("Node"+(addedTypes.length!=1 ? "s":"")+" added to palette:"+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("Node"+(m.types.length!=1 ? "s":"")+" removed from palette:"+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("Node"+(msg.types.length!=1 ? "s":"")+" enabled:"+typeList,"success");
|
||||
} else {
|
||||
$.get('nodes/'+msg.id, function(data) {
|
||||
$("body").append(data);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" added to palette:"+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("Node"+(msg.types.length!=1 ? "s":"")+" disabled:"+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-sidebar",label:"Sidebar",toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true},
|
||||
{id:"menu-item-status",label:"Display node status",toggle:true,onselect:toggleStatus, selected: true},
|
||||
null,
|
||||
{id:"menu-item-import",label:"Import",options:[
|
||||
{id:"menu-item-import-clipboard",label:"Clipboard",onselect:RED.clipboard.import},
|
||||
{id:"menu-item-import-library",label:"Library",options:[]}
|
||||
]},
|
||||
{id:"menu-item-export",label:"Export",disabled:true,options:[
|
||||
{id:"menu-item-export-clipboard",label:"Clipboard",disabled:true,onselect:RED.clipboard.export},
|
||||
{id:"menu-item-export-library",label:"Library",disabled:true,onselect:RED.library.export}
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show},
|
||||
null,
|
||||
{id:"menu-item-subflow",label:"Subflows", options: [
|
||||
{id:"menu-item-subflow-create",label:"Create subflow",onselect:RED.subflow.createSubflow},
|
||||
{id:"menu-item-subflow-convert",label:"Selection to subflow",disabled:true,onselect:RED.subflow.convertToSubflow},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-workspace",label:"Workspaces",options:[
|
||||
{id:"menu-item-workspace-add",label:"Add",onselect:RED.workspaces.add},
|
||||
{id:"menu-item-workspace-edit",label:"Rename",onselect:RED.workspaces.edit},
|
||||
{id:"menu-item-workspace-delete",label:"Delete",onselect:RED.workspaces.remove},
|
||||
null
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-keyboard-shortcuts",label:"Keyboard Shortcuts",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")
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
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.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.settings.init(loadEditor);
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
};
|
||||
})();
|
926
editor/js/nodes.js
Normal file
@@ -0,0 +1,926 @@
|
||||
/**
|
||||
* 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.nodes = (function() {
|
||||
|
||||
var node_defs = {};
|
||||
var nodes = [];
|
||||
var configNodes = {};
|
||||
var links = [];
|
||||
var defaultWorkspace;
|
||||
var workspaces = {};
|
||||
var subflows = {};
|
||||
|
||||
var dirty = false;
|
||||
|
||||
function setDirty(d) {
|
||||
dirty = d;
|
||||
eventHandler.emit("change",{dirty:dirty});
|
||||
}
|
||||
|
||||
var registry = (function() {
|
||||
var nodeList = [];
|
||||
var nodeSets = {};
|
||||
var typeToId = {};
|
||||
var nodeDefinitions = {};
|
||||
|
||||
var exports = {
|
||||
getNodeList: function() {
|
||||
return nodeList;
|
||||
},
|
||||
setNodeList: function(list) {
|
||||
nodeList = [];
|
||||
for(var i=0;i<list.length;i++) {
|
||||
var ns = list[i];
|
||||
exports.addNodeSet(ns);
|
||||
}
|
||||
},
|
||||
addNodeSet: function(ns) {
|
||||
ns.added = false;
|
||||
nodeSets[ns.id] = ns;
|
||||
for (var j=0;j<ns.types.length;j++) {
|
||||
typeToId[ns.types[j]] = ns.id;
|
||||
}
|
||||
nodeList.push(ns);
|
||||
},
|
||||
removeNodeSet: function(id) {
|
||||
var ns = nodeSets[id];
|
||||
for (var j=0;j<ns.types.length;j++) {
|
||||
if (ns.added) {
|
||||
// TODO: too tightly coupled into palette UI
|
||||
RED.palette.remove(ns.types[j]);
|
||||
var def = nodeDefinitions[ns.types[j]];
|
||||
if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
|
||||
def.onpaletteremove.call(def);
|
||||
}
|
||||
}
|
||||
delete typeToId[ns.types[j]];
|
||||
}
|
||||
delete nodeSets[id];
|
||||
for (var i=0;i<nodeList.length;i++) {
|
||||
if (nodeList[i].id == id) {
|
||||
nodeList.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ns;
|
||||
},
|
||||
getNodeSet: function(id) {
|
||||
return nodeSets[id];
|
||||
},
|
||||
enableNodeSet: function(id) {
|
||||
var ns = nodeSets[id];
|
||||
ns.enabled = true;
|
||||
for (var j=0;j<ns.types.length;j++) {
|
||||
// TODO: too tightly coupled into palette UI
|
||||
RED.palette.show(ns.types[j]);
|
||||
var def = nodeDefinitions[ns.types[j]];
|
||||
if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
|
||||
def.onpaletteadd.call(def);
|
||||
}
|
||||
}
|
||||
},
|
||||
disableNodeSet: function(id) {
|
||||
var ns = nodeSets[id];
|
||||
ns.enabled = false;
|
||||
for (var j=0;j<ns.types.length;j++) {
|
||||
// TODO: too tightly coupled into palette UI
|
||||
RED.palette.hide(ns.types[j]);
|
||||
var def = nodeDefinitions[ns.types[j]];
|
||||
if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
|
||||
def.onpaletteremove.call(def);
|
||||
}
|
||||
}
|
||||
},
|
||||
registerNodeType: function(nt,def) {
|
||||
nodeDefinitions[nt] = def;
|
||||
if (def.category != "subflows") {
|
||||
nodeSets[typeToId[nt]].added = true;
|
||||
// TODO: too tightly coupled into palette UI
|
||||
}
|
||||
RED.palette.add(nt,def);
|
||||
if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
|
||||
def.onpaletteadd.call(def);
|
||||
}
|
||||
},
|
||||
removeNodeType: function(nt) {
|
||||
if (nt.substring(0,8) != "subflow:") {
|
||||
throw new Error("this api is subflow only. called with:",nt);
|
||||
}
|
||||
delete nodeDefinitions[nt];
|
||||
RED.palette.remove(nt);
|
||||
},
|
||||
getNodeType: function(nt) {
|
||||
return nodeDefinitions[nt];
|
||||
}
|
||||
};
|
||||
return exports;
|
||||
})();
|
||||
|
||||
function getID() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
function addNode(n) {
|
||||
if (n._def.category == "config") {
|
||||
configNodes[n.id] = n;
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
n.dirty = true;
|
||||
var updatedConfigNode = false;
|
||||
for (var d in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d)) {
|
||||
var property = n._def.defaults[d];
|
||||
if (property.type) {
|
||||
var type = registry.getNodeType(property.type);
|
||||
if (type && type.category == "config") {
|
||||
var configNode = configNodes[n[d]];
|
||||
if (configNode) {
|
||||
updatedConfigNode = true;
|
||||
configNode.users.push(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
if (n._def.category == "subflows" && typeof n.i === "undefined") {
|
||||
var nextId = 0;
|
||||
RED.nodes.eachNode(function(node) {
|
||||
nextId = Math.max(nextId,node.i||0);
|
||||
});
|
||||
n.i = nextId+1;
|
||||
}
|
||||
nodes.push(n);
|
||||
}
|
||||
if (n._def.onadd) {
|
||||
n._def.onadd.call(n);
|
||||
}
|
||||
}
|
||||
function addLink(l) {
|
||||
links.push(l);
|
||||
}
|
||||
|
||||
function getNode(id) {
|
||||
if (id in configNodes) {
|
||||
return configNodes[id];
|
||||
} else {
|
||||
for (var n in nodes) {
|
||||
if (nodes[n].id == id) {
|
||||
return nodes[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeNode(id) {
|
||||
var removedLinks = [];
|
||||
var node;
|
||||
if (id in configNodes) {
|
||||
node = configNodes[id];
|
||||
delete configNodes[id];
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
node = getNode(id);
|
||||
if (node) {
|
||||
nodes.splice(nodes.indexOf(node),1);
|
||||
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
||||
removedLinks.forEach(function(l) {links.splice(links.indexOf(l), 1); });
|
||||
var updatedConfigNode = false;
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d)) {
|
||||
var property = node._def.defaults[d];
|
||||
if (property.type) {
|
||||
var type = registry.getNodeType(property.type);
|
||||
if (type && type.category == "config") {
|
||||
var configNode = configNodes[node[d]];
|
||||
if (configNode) {
|
||||
updatedConfigNode = true;
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(node),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node._def.onremove) {
|
||||
node._def.onremove.call(n);
|
||||
}
|
||||
return removedLinks;
|
||||
}
|
||||
|
||||
function removeLink(l) {
|
||||
var index = links.indexOf(l);
|
||||
if (index != -1) {
|
||||
links.splice(index,1);
|
||||
}
|
||||
}
|
||||
|
||||
function addWorkspace(ws) {
|
||||
workspaces[ws.id] = ws;
|
||||
}
|
||||
function getWorkspace(id) {
|
||||
return workspaces[id];
|
||||
}
|
||||
function removeWorkspace(id) {
|
||||
delete workspaces[id];
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var n;
|
||||
for (n=0;n<nodes.length;n++) {
|
||||
var node = nodes[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);
|
||||
}
|
||||
return {nodes:removedNodes,links:removedLinks};
|
||||
}
|
||||
|
||||
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:""}},
|
||||
icon:"subflow.png",
|
||||
category: "subflows",
|
||||
inputs: sf.in.length,
|
||||
outputs: sf.out.length,
|
||||
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 }
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function getSubflow(id) {
|
||||
return subflows[id];
|
||||
}
|
||||
function removeSubflow(sf) {
|
||||
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];
|
||||
if (node.z === sfid) {
|
||||
var m = /^subflow:(.+)$/.exec(node.type);
|
||||
if (m) {
|
||||
if (m[1] === nodeid) {
|
||||
return true;
|
||||
} else {
|
||||
var result = subflowContains(m[1],nodeid);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAllFlowNodes(node) {
|
||||
var visited = {};
|
||||
visited[node.id] = true;
|
||||
var nns = [node];
|
||||
var stack = [node];
|
||||
while(stack.length !== 0) {
|
||||
var n = stack.shift();
|
||||
var childLinks = links.filter(function(d) { return (d.source === n) || (d.target === n);});
|
||||
for (var i=0;i<childLinks.length;i++) {
|
||||
var child = (childLinks[i].source === n)?childLinks[i].target:childLinks[i].source;
|
||||
var id = child.id;
|
||||
if (!id) {
|
||||
id = child.direction+":"+child.i;
|
||||
}
|
||||
if (!visited[id]) {
|
||||
visited[id] = true;
|
||||
nns.push(child);
|
||||
stack.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a node to an exportable JSON Object
|
||||
**/
|
||||
function convertNode(n, exportCreds) {
|
||||
exportCreds = exportCreds || false;
|
||||
var node = {};
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
if (node.type == "unknown") {
|
||||
for (var p in n._orig) {
|
||||
if (n._orig.hasOwnProperty(p)) {
|
||||
node[p] = n._orig[p];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var d in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d)) {
|
||||
node[d] = n[d];
|
||||
}
|
||||
}
|
||||
if(exportCreds && n.credentials) {
|
||||
var credentialSet = {};
|
||||
node.credentials = {};
|
||||
for (var cred in n._def.credentials) {
|
||||
if (n._def.credentials.hasOwnProperty(cred)) {
|
||||
if (n._def.credentials[cred].type == 'password') {
|
||||
if (n.credentials["has_"+cred] != n.credentials._["has_"+cred] ||
|
||||
(n.credentials["has_"+cred] && n.credentials[cred])) {
|
||||
credentialSet[cred] = n.credentials[cred];
|
||||
}
|
||||
} else if (n.credentials[cred] != null && n.credentials[cred] != n.credentials._[cred]) {
|
||||
credentialSet[cred] = n.credentials[cred];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(credentialSet).length > 0) {
|
||||
node.credentials = credentialSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
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([]);
|
||||
}
|
||||
var wires = links.filter(function(d){return d.source === n;});
|
||||
for (var j=0;j<wires.length;j++) {
|
||||
var w = wires[j];
|
||||
if (w.target.type != "subflow") {
|
||||
node.wires[w.sourcePort].push(w.target.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function convertSubflow(n) {
|
||||
var node = {};
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.name = n.name;
|
||||
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 });
|
||||
for (var i=0;i<wires.length;i++) {
|
||||
var w = wires[i];
|
||||
if (w.target.type != "subflow") {
|
||||
nIn.wires.push({id:w.target.id})
|
||||
}
|
||||
}
|
||||
node.in.push(nIn);
|
||||
});
|
||||
n.out.forEach(function(p,c) {
|
||||
var nOut = {x:p.x,y:p.y,wires:[]};
|
||||
var wires = links.filter(function(d) { return d.target === p });
|
||||
for (i=0;i<wires.length;i++) {
|
||||
if (wires[i].source.type != "subflow") {
|
||||
nOut.wires.push({id:wires[i].source.id,port:wires[i].sourcePort})
|
||||
} else {
|
||||
nOut.wires.push({id:n.id,port:0})
|
||||
}
|
||||
}
|
||||
node.out.push(nOut);
|
||||
});
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* Converts the current node selection to an exportable JSON Object
|
||||
**/
|
||||
function createExportableNodeSet(set) {
|
||||
var nns = [];
|
||||
var exportedConfigNodes = {};
|
||||
var exportedSubflows = {};
|
||||
for (var n=0;n<set.length;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 = [subflow];
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.z == subflowId) {
|
||||
subflowSet.push(n);
|
||||
}
|
||||
});
|
||||
var exportableSubflow = createExportableNodeSet(subflowSet);
|
||||
nns = exportableSubflow.concat(nns);
|
||||
}
|
||||
}
|
||||
if (node.type != "subflow") {
|
||||
var convertedNode = RED.nodes.convertNode(node);
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults[d].type && node[d] in configNodes) {
|
||||
var confNode = configNodes[node[d]];
|
||||
var exportable = registry.getNodeType(node._def.defaults[d].type).exportable;
|
||||
if ((exportable == null || exportable)) {
|
||||
if (!(node[d] in exportedConfigNodes)) {
|
||||
exportedConfigNodes[node[d]] = true;
|
||||
nns.unshift(RED.nodes.convertNode(confNode));
|
||||
}
|
||||
} else {
|
||||
convertedNode[d] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
nns.push(convertedNode);
|
||||
} else {
|
||||
var convertedSubflow = convertSubflow(node);
|
||||
nns.push(convertedSubflow);
|
||||
}
|
||||
}
|
||||
return nns;
|
||||
}
|
||||
|
||||
//TODO: rename this (createCompleteNodeSet)
|
||||
function createCompleteNodeSet() {
|
||||
var nns = [];
|
||||
var i;
|
||||
for (i in workspaces) {
|
||||
if (workspaces.hasOwnProperty(i)) {
|
||||
if (workspaces[i].type == "tab") {
|
||||
nns.push(workspaces[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i in subflows) {
|
||||
if (subflows.hasOwnProperty(i)) {
|
||||
nns.push(convertSubflow(subflows[i]));
|
||||
}
|
||||
}
|
||||
for (i in configNodes) {
|
||||
if (configNodes.hasOwnProperty(i)) {
|
||||
nns.push(convertNode(configNodes[i], true));
|
||||
}
|
||||
}
|
||||
for (i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
nns.push(convertNode(node, true));
|
||||
}
|
||||
return nns;
|
||||
}
|
||||
|
||||
function importNodes(newNodesObj,createNewIds) {
|
||||
var i;
|
||||
var n;
|
||||
var newNodes;
|
||||
if (typeof newNodesObj === "string") {
|
||||
if (newNodesObj === "") {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
newNodes = JSON.parse(newNodesObj);
|
||||
} catch(err) {
|
||||
var e = new Error("Invalid flow: "+err.message);
|
||||
e.code = "NODE_RED";
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
newNodes = newNodesObj;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.workspaces.active();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var new_workspaces = [];
|
||||
var workspace_map = {};
|
||||
var new_subflows = [];
|
||||
var subflow_map = {};
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 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") {
|
||||
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.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
|
||||
}
|
||||
} else {
|
||||
node._def = {
|
||||
category:"config"
|
||||
};
|
||||
node.users = [];
|
||||
}
|
||||
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)) {
|
||||
node[d2] = n[d2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (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++) {
|
||||
if (wires[w2] in node_map) {
|
||||
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
});
|
||||
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);
|
||||
});
|
||||
delete output.wires;
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: DRY
|
||||
var eventHandler = (function() {
|
||||
var handlers = {};
|
||||
|
||||
return {
|
||||
on: function(evt,func) {
|
||||
handlers[evt] = handlers[evt]||[];
|
||||
handlers[evt].push(func);
|
||||
},
|
||||
emit: function(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
handlers[evt][i](arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
on: eventHandler.on,
|
||||
|
||||
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]);
|
||||
}
|
||||
},
|
||||
eachLink: function(cb) {
|
||||
for (var l=0;l<links.length;l++) {
|
||||
cb(links[l]);
|
||||
}
|
||||
},
|
||||
eachConfig: function(cb) {
|
||||
for (var id in configNodes) {
|
||||
if (configNodes.hasOwnProperty(id)) {
|
||||
cb(configNodes[id]);
|
||||
}
|
||||
}
|
||||
},
|
||||
eachSubflow: function(cb) {
|
||||
for (var id in subflows) {
|
||||
if (subflows.hasOwnProperty(id)) {
|
||||
cb(subflows[id]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
node: getNode,
|
||||
|
||||
filterNodes: filterNodes,
|
||||
filterLinks: filterLinks,
|
||||
|
||||
import: importNodes,
|
||||
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
id: getID,
|
||||
dirty: function(d) {
|
||||
if (d == null) {
|
||||
return dirty;
|
||||
} else {
|
||||
setDirty(d);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
148
editor/js/settings.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright 2014 IBM, Antoine Aflalo
|
||||
*
|
||||
* 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.settings = (function () {
|
||||
|
||||
var loadedSettings = {};
|
||||
|
||||
var hasLocalStorage = function () {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var set = function (key, value) {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
||||
/**
|
||||
* If the key is not set in the localStorage it returns <i>undefined</i>
|
||||
* Else return the JSON parsed value
|
||||
* @param key
|
||||
* @returns {*}
|
||||
*/
|
||||
var get = function (key) {
|
||||
if (!hasLocalStorage()) {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(localStorage.getItem(key));
|
||||
};
|
||||
|
||||
var remove = function (key) {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem(key);
|
||||
};
|
||||
|
||||
var setProperties = function(data) {
|
||||
for (var prop in loadedSettings) {
|
||||
if (loadedSettings.hasOwnProperty(prop) && RED.settings.hasOwnProperty(prop)) {
|
||||
delete RED.settings[prop];
|
||||
}
|
||||
}
|
||||
for (prop in data) {
|
||||
if (data.hasOwnProperty(prop)) {
|
||||
RED.settings[prop] = data[prop];
|
||||
}
|
||||
}
|
||||
loadedSettings = data;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
load(done);
|
||||
}
|
||||
|
||||
var load = function(done) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
},
|
||||
dataType: "json",
|
||||
cache: false,
|
||||
url: 'settings',
|
||||
success: function (data) {
|
||||
setProperties(data);
|
||||
if (RED.settings.user && RED.settings.user.anonymous) {
|
||||
RED.settings.remove("auth-tokens");
|
||||
}
|
||||
console.log("Node-RED: " + data.version);
|
||||
done();
|
||||
},
|
||||
error: function(jqXHR,textStatus,errorThrown) {
|
||||
if (jqXHR.status === 401) {
|
||||
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
|
||||
window.location.search = "";
|
||||
}
|
||||
RED.user.login(function() { load(done); });
|
||||
} else {
|
||||
console.log("Unexpected error:",jqXHR.status,textStatus);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
|
||||
theme: theme
|
||||
}
|
||||
})
|
||||
();
|
178
editor/js/ui/clipboard.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* 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 = $('<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: "Ok",
|
||||
click: function() {
|
||||
if (/Import/.test(dialog.dialog("option","title"))) {
|
||||
RED.view.importNodes($("#clipboard-import").val());
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-cancel",
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-close",
|
||||
text: "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();
|
||||
}
|
||||
});
|
||||
|
||||
var dialogContainer = dialog.children(".dialog-form");
|
||||
|
||||
var exportNodesDialog = '<div class="form-row">'+
|
||||
'<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> 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">'+
|
||||
'Select the text above and copy to the clipboard with Ctrl-C.'+
|
||||
'</div>';
|
||||
|
||||
var 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="Paste nodes here"></textarea>'+
|
||||
'</div>';
|
||||
|
||||
|
||||
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(function() {
|
||||
var v = $(this).val();
|
||||
try {
|
||||
JSON.parse(v);
|
||||
$(this).removeClass("input-error");
|
||||
$("#clipboard-dialog-ok").button("enable");
|
||||
} catch(err) {
|
||||
if (v !== "") {
|
||||
$(this).addClass("input-error");
|
||||
}
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
}
|
||||
});
|
||||
dialog.dialog("option","title","Import nodes").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","Export nodes to clipboard").dialog( "open" );
|
||||
}
|
||||
}
|
||||
|
||||
function hideDropTarget() {
|
||||
$("#dropTarget").hide();
|
||||
RED.keyboard.remove(/* ESCAPE */ 27);
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.view.on("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();
|
||||
RED.view.importNodes(data);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
import: importNodes,
|
||||
export: exportNodes
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})();
|
253
editor/js/ui/deploy.js
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* 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.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>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:"Full",sublabel:"Deploys everything in the workspace",selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
|
||||
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.png",label:"Modified Flows",sublabel:"Only deploys flows that contain changed nodes", onselect:function(s) {if(s){changeDeploymentType("flows")}}},
|
||||
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.png",label:"Modified Nodes",sublabel:"Only deploys nodes that have changed",onselect:function(s) { if(s){changeDeploymentType("nodes")}}}
|
||||
]
|
||||
});
|
||||
} else if (type == "simple") {
|
||||
var label = options.label || "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: "Confirm deploy",
|
||||
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: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
create: function() {
|
||||
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
|
||||
.append('<div style="height:0; vertical-align: middle; display:inline-block;">'+
|
||||
'<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.nodes.on('change',function(state) {
|
||||
if (state.dirty) {
|
||||
window.onbeforeunload = function() {
|
||||
return "You have undeployed changes.\n\nLeaving this page will lose these changes.";
|
||||
}
|
||||
$("#btn-deploy").removeClass("disabled");
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
$("#btn-deploy").addClass("disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 = [];
|
||||
RED.nodes.eachNode(function(node) {
|
||||
hasInvalid = hasInvalid || !node.valid;
|
||||
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) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
unusedConfigNodes[node.type] = unusedConfigNodes[node.type] || [];
|
||||
unusedConfigNodes[node.type].push(label);
|
||||
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();
|
||||
} else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
$( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
var unusedNodeLabels = [];
|
||||
var unusedTypes = Object.keys(unusedConfigNodes).sort();
|
||||
unusedTypes.forEach(function(type) {
|
||||
unusedConfigNodes[type].forEach(function(label) {
|
||||
unusedNodeLabels.push(type+": "+label);
|
||||
});
|
||||
});
|
||||
$( "#node-dialog-confirm-deploy-unused-list" )
|
||||
.html("<li>"+unusedNodeLabels.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) {
|
||||
RED.notify("Successfully deployed","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();
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.nodes.dirty(true);
|
||||
if (xhr.responseText) {
|
||||
RED.notify("<strong>Error</strong>: "+xhr.responseJSON.message,"error");
|
||||
} else {
|
||||
RED.notify("<strong>Error</strong>: no response from server","error");
|
||||
}
|
||||
}).always(function() {
|
||||
$("#btn-deploy-icon").removeClass('spinner');
|
||||
$("#btn-deploy-icon").addClass('fa-download');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
})();
|
943
editor/js/ui/editor.js
Normal file
@@ -0,0 +1,943 @@
|
||||
/**
|
||||
* 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.editor = (function() {
|
||||
var editing_node = null;
|
||||
|
||||
function getCredentialsURL(nodeType, nodeID) {
|
||||
var dashedType = nodeType.replace(/\s+/g, '-');
|
||||
return 'credentials/' + dashedType + "/" + nodeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a node
|
||||
* @param node - the node being validated
|
||||
* @returns {boolean} whether the node is valid. Sets node.dirty if needed
|
||||
*/
|
||||
function validateNode(node) {
|
||||
var oldValue = node.valid;
|
||||
var oldChanged = node.changed;
|
||||
node.valid = true;
|
||||
var subflow;
|
||||
var isValid;
|
||||
var hasChanged;
|
||||
|
||||
if (node.type.indexOf("subflow:")===0) {
|
||||
subflow = RED.nodes.subflow(node.type.substring(8));
|
||||
isValid = subflow.valid;
|
||||
hasChanged = subflow.changed;
|
||||
if (isValid === undefined) {
|
||||
isValid = validateNode(subflow);
|
||||
hasChanged = subflow.changed;
|
||||
}
|
||||
node.valid = isValid;
|
||||
node.changed = hasChanged;
|
||||
} else if (node._def) {
|
||||
node.valid = validateNodeProperties(node, node._def.defaults, node);
|
||||
if (node._def._creds) {
|
||||
node.valid = node.valid && validateNodeProperties(node, node._def.credentials, node._def._creds);
|
||||
}
|
||||
} else if (node.type == "subflow") {
|
||||
var subflowNodes = RED.nodes.filterNodes({z:node.id});
|
||||
for (var i=0;i<subflowNodes.length;i++) {
|
||||
isValid = subflowNodes[i].valid;
|
||||
hasChanged = subflowNodes[i].changed;
|
||||
if (isValid === undefined) {
|
||||
isValid = validateNode(subflowNodes[i]);
|
||||
hasChanged = subflowNodes[i].changed;
|
||||
}
|
||||
node.valid = node.valid && isValid;
|
||||
node.changed = node.changed || hasChanged;
|
||||
}
|
||||
var subflowInstances = RED.nodes.filterNodes({type:"subflow:"+node.id});
|
||||
var modifiedTabs = {};
|
||||
for (i=0;i<subflowInstances.length;i++) {
|
||||
subflowInstances[i].valid = node.valid;
|
||||
subflowInstances[i].changed = node.changed;
|
||||
subflowInstances[i].dirty = true;
|
||||
modifiedTabs[subflowInstances[i].z] = true;
|
||||
}
|
||||
Object.keys(modifiedTabs).forEach(function(id) {
|
||||
var subflow = RED.nodes.subflow(id);
|
||||
if (subflow) {
|
||||
validateNode(subflow);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (oldValue !== node.valid || oldChanged !== node.changed) {
|
||||
node.dirty = true;
|
||||
subflow = RED.nodes.subflow(node.z);
|
||||
if (subflow) {
|
||||
validateNode(subflow);
|
||||
}
|
||||
}
|
||||
return node.valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a node's properties for the given set of property definitions
|
||||
* @param node - the node being validated
|
||||
* @param definition - the node property definitions (either def.defaults or def.creds)
|
||||
* @param properties - the node property values to validate
|
||||
* @returns {boolean} whether the node's properties are valid
|
||||
*/
|
||||
function validateNodeProperties(node, definition, properties) {
|
||||
var isValid = true;
|
||||
for (var prop in definition) {
|
||||
if (definition.hasOwnProperty(prop)) {
|
||||
if (!validateNodeProperty(node, definition, prop, properties[prop])) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a individual node property
|
||||
* @param node - the node being validated
|
||||
* @param definition - the node property definitions (either def.defaults or def.creds)
|
||||
* @param property - the property name being validated
|
||||
* @param value - the property value being validated
|
||||
* @returns {boolean} whether the node proprty is valid
|
||||
*/
|
||||
function validateNodeProperty(node,definition,property,value) {
|
||||
var valid = true;
|
||||
if ("required" in definition[property] && definition[property].required) {
|
||||
valid = value !== "";
|
||||
}
|
||||
if (valid && "validate" in definition[property]) {
|
||||
valid = definition[property].validate.call(node,value);
|
||||
}
|
||||
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
|
||||
if (!value || value == "_ADD_") {
|
||||
valid = definition[property].hasOwnProperty("required") && !definition[property].required;
|
||||
} else {
|
||||
var v = RED.nodes.node(value).valid;
|
||||
valid = (v==null || v);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the node's properties have changed.
|
||||
* Marks the node as dirty and needing a size check.
|
||||
* Removes any links to non-existant outputs.
|
||||
* @param node - the node that has been updated
|
||||
* @returns {array} the links that were removed due to this update
|
||||
*/
|
||||
function updateNodeProperties(node) {
|
||||
node.resize = true;
|
||||
node.dirty = true;
|
||||
var removedLinks = [];
|
||||
if (node.ports) {
|
||||
if (node.outputs < node.ports.length) {
|
||||
while (node.outputs < node.ports.length) {
|
||||
node.ports.pop();
|
||||
}
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source === node && l.sourcePort >= node.outputs) {
|
||||
removedLinks.push(l);
|
||||
}
|
||||
});
|
||||
} else if (node.outputs > node.ports.length) {
|
||||
while (node.outputs > node.ports.length) {
|
||||
node.ports.push(node.ports.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.inputs === 0) {
|
||||
removedLinks.concat(RED.nodes.filterLinks({target:node}));
|
||||
}
|
||||
for (var l=0;l<removedLinks.length;l++) {
|
||||
RED.nodes.removeLink(removedLinks[l]);
|
||||
}
|
||||
return removedLinks;
|
||||
}
|
||||
|
||||
$( "#dialog" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
dialogClass: "ui-dialog-no-close",
|
||||
closeOnEscape: false,
|
||||
minWidth: 500,
|
||||
width: 'auto',
|
||||
buttons: [
|
||||
{
|
||||
id: "node-dialog-ok",
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
if (editing_node) {
|
||||
var changes = {};
|
||||
var changed = false;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var d;
|
||||
|
||||
if (editing_node._def.oneditsave) {
|
||||
var oldValues = {};
|
||||
for (d in editing_node._def.defaults) {
|
||||
if (editing_node._def.defaults.hasOwnProperty(d)) {
|
||||
if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
|
||||
oldValues[d] = editing_node[d];
|
||||
} else {
|
||||
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
|
||||
}
|
||||
}
|
||||
}
|
||||
var rc = editing_node._def.oneditsave.call(editing_node);
|
||||
if (rc === true) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
for (d in editing_node._def.defaults) {
|
||||
if (editing_node._def.defaults.hasOwnProperty(d)) {
|
||||
if (oldValues[d] === null || typeof oldValues[d] === "string" || typeof oldValues[d] === "number") {
|
||||
if (oldValues[d] !== editing_node[d]) {
|
||||
changes[d] = oldValues[d];
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (JSON.stringify(oldValues[d]) !== JSON.stringify(editing_node[d])) {
|
||||
changes[d] = oldValues[d];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editing_node._def.defaults) {
|
||||
for (d in editing_node._def.defaults) {
|
||||
if (editing_node._def.defaults.hasOwnProperty(d)) {
|
||||
var input = $("#node-input-"+d);
|
||||
var newValue;
|
||||
if (input.attr('type') === "checkbox") {
|
||||
newValue = input.prop('checked');
|
||||
} else {
|
||||
newValue = input.val();
|
||||
}
|
||||
if (newValue != null) {
|
||||
if (editing_node[d] != newValue) {
|
||||
if (editing_node._def.defaults[d].type) {
|
||||
if (newValue == "_ADD_") {
|
||||
newValue = "";
|
||||
}
|
||||
// Change to a related config node
|
||||
var configNode = RED.nodes.node(editing_node[d]);
|
||||
if (configNode) {
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(editing_node),1);
|
||||
}
|
||||
configNode = RED.nodes.node(newValue);
|
||||
if (configNode) {
|
||||
configNode.users.push(editing_node);
|
||||
}
|
||||
}
|
||||
changes[d] = editing_node[d];
|
||||
editing_node[d] = newValue;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (editing_node._def.credentials) {
|
||||
var prefix = 'node-input';
|
||||
var credDefinition = editing_node._def.credentials;
|
||||
var credsChanged = updateNodeCredentials(editing_node,credDefinition,prefix);
|
||||
changed = changed || credsChanged;
|
||||
}
|
||||
|
||||
var removedLinks = updateNodeProperties(editing_node);
|
||||
if (changed) {
|
||||
var wasChanged = editing_node.changed;
|
||||
editing_node.changed = true;
|
||||
RED.nodes.dirty(true);
|
||||
RED.history.push({t:'edit',node:editing_node,changes:changes,links:removedLinks,dirty:wasDirty,changed:wasChanged});
|
||||
}
|
||||
editing_node.dirty = true;
|
||||
validateNode(editing_node);
|
||||
RED.view.redraw();
|
||||
} else if (/Export nodes to library/.test($( "#dialog" ).dialog("option","title"))) {
|
||||
//TODO: move this to RED.library
|
||||
var flowName = $("#node-input-filename").val();
|
||||
if (!/^\s*$/.test(flowName)) {
|
||||
$.ajax({
|
||||
url:'library/flows/'+flowName,
|
||||
type: "POST",
|
||||
data: $("#node-input-filename").attr('nodes'),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function() {
|
||||
RED.library.loadFlowLibrary();
|
||||
RED.notify("Saved nodes","success");
|
||||
});
|
||||
}
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-dialog-cancel",
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
if (editing_node && editing_node._def) {
|
||||
if (editing_node._def.oneditcancel) {
|
||||
editing_node._def.oneditcancel.call(editing_node);
|
||||
}
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
resize: function(e,ui) {
|
||||
if (editing_node) {
|
||||
$(this).dialog('option',"sizeCache-"+editing_node.type,ui.size);
|
||||
}
|
||||
},
|
||||
open: function(e) {
|
||||
var minWidth = $(this).dialog('option','minWidth');
|
||||
if ($(this).outerWidth() < minWidth) {
|
||||
$(this).dialog('option','width',minWidth);
|
||||
} else {
|
||||
$(this).dialog('option','width',$(this).outerWidth());
|
||||
}
|
||||
RED.keyboard.disable();
|
||||
if (editing_node) {
|
||||
var size = $(this).dialog('option','sizeCache-'+editing_node.type);
|
||||
if (size) {
|
||||
$(this).dialog('option','width',size.width);
|
||||
$(this).dialog('option','height',size.height);
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
|
||||
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
|
||||
RED.view.state(RED.state.DEFAULT);
|
||||
}
|
||||
$( this ).dialog('option','height','auto');
|
||||
$( this ).dialog('option','width','auto');
|
||||
if (editing_node) {
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
}
|
||||
RED.sidebar.config.refresh();
|
||||
|
||||
var buttons = $( this ).dialog("option","buttons");
|
||||
if (buttons.length == 3) {
|
||||
$( this ).dialog("option","buttons",buttons.splice(1));
|
||||
}
|
||||
editing_node = null;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a config-node select box for this property
|
||||
* @param node - the node being edited
|
||||
* @param property - the name of the field
|
||||
* @param type - the type of the config-node
|
||||
*/
|
||||
function prepareConfigNodeSelect(node,property,type) {
|
||||
var input = $("#node-input-"+property);
|
||||
var node_def = RED.nodes.getType(type);
|
||||
|
||||
input.replaceWith('<select style="width: 60%;" id="node-input-'+property+'"></select>');
|
||||
updateConfigNodeSelect(property,type,node[property]);
|
||||
var select = $("#node-input-"+property);
|
||||
select.after(' <a id="node-input-lookup-'+property+'" class="btn"><i class="fa fa-pencil"></i></a>');
|
||||
$('#node-input-lookup-'+property).click(function(e) {
|
||||
showEditConfigNodeDialog(property,type,select.find(":selected").val());
|
||||
e.preventDefault();
|
||||
});
|
||||
var label = "";
|
||||
var configNode = RED.nodes.node(node[property]);
|
||||
if (configNode && node_def.label) {
|
||||
if (typeof node_def.label == "function") {
|
||||
label = node_def.label.call(configNode);
|
||||
} else {
|
||||
label = node_def.label;
|
||||
}
|
||||
}
|
||||
input.val(label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a config-node button for this property
|
||||
* @param node - the node being edited
|
||||
* @param property - the name of the field
|
||||
* @param type - the type of the config-node
|
||||
*/
|
||||
function prepareConfigNodeButton(node,property,type) {
|
||||
var input = $("#node-input-"+property);
|
||||
input.val(node[property]);
|
||||
input.attr("type","hidden");
|
||||
|
||||
var button = $("<a>",{id:"node-input-edit-"+property, class:"btn"});
|
||||
input.after(button);
|
||||
|
||||
if (node[property]) {
|
||||
button.text("edit");
|
||||
} else {
|
||||
button.text("add");
|
||||
}
|
||||
|
||||
button.click(function(e) {
|
||||
showEditConfigNodeDialog(property,type,input.val()||"_ADD_");
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the editor dialog input field for this property
|
||||
* @param node - the node being edited
|
||||
* @param property - the name of the field
|
||||
* @param prefix - the prefix to use in the input element ids (node-input|node-config-input)
|
||||
*/
|
||||
function preparePropertyEditor(node,property,prefix) {
|
||||
var input = $("#"+prefix+"-"+property);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
input.prop('checked',node[property]);
|
||||
} else {
|
||||
var val = node[property];
|
||||
if (val == null) {
|
||||
val = "";
|
||||
}
|
||||
input.val(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an on-change handler to revalidate a node field
|
||||
* @param node - the node being edited
|
||||
* @param definition - the definition of the node
|
||||
* @param property - the name of the field
|
||||
* @param prefix - the prefix to use in the input element ids (node-input|node-config-input)
|
||||
*/
|
||||
function attachPropertyChangeHandler(node,definition,property,prefix) {
|
||||
$("#"+prefix+"-"+property).change(function() {
|
||||
if (!validateNodeProperty(node, definition, property,this.value)) {
|
||||
$(this).addClass("input-error");
|
||||
} else {
|
||||
$(this).removeClass("input-error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the value to each credential field
|
||||
* @param node
|
||||
* @param credDef
|
||||
* @param credData
|
||||
* @param prefix
|
||||
*/
|
||||
function populateCredentialsInputs(node, credDef, credData, prefix) {
|
||||
var cred;
|
||||
for (cred in credDef) {
|
||||
if (credDef.hasOwnProperty(cred)) {
|
||||
if (credDef[cred].type == 'password') {
|
||||
if (credData[cred]) {
|
||||
$('#' + prefix + '-' + cred).val(credData[cred]);
|
||||
} else if (credData['has_' + cred]) {
|
||||
$('#' + prefix + '-' + cred).val('__PWRD__');
|
||||
}
|
||||
else {
|
||||
$('#' + prefix + '-' + cred).val('');
|
||||
}
|
||||
} else {
|
||||
preparePropertyEditor(credData, cred, prefix);
|
||||
}
|
||||
attachPropertyChangeHandler(node, credDef, cred, prefix);
|
||||
}
|
||||
}
|
||||
for (cred in credDef) {
|
||||
if (credDef.hasOwnProperty(cred)) {
|
||||
$("#" + prefix + "-" + cred).change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the node credentials from the edit form
|
||||
* @param node - the node containing the credentials
|
||||
* @param credDefinition - definition of the credentials
|
||||
* @param prefix - prefix of the input fields
|
||||
* @return {boolean} whether anything has changed
|
||||
*/
|
||||
function updateNodeCredentials(node, credDefinition, prefix) {
|
||||
var changed = false;
|
||||
if(!node.credentials) {
|
||||
node.credentials = {_:{}};
|
||||
}
|
||||
|
||||
for (var cred in credDefinition) {
|
||||
if (credDefinition.hasOwnProperty(cred)) {
|
||||
var input = $("#" + prefix + '-' + cred);
|
||||
var value = input.val();
|
||||
if (credDefinition[cred].type == 'password') {
|
||||
node.credentials['has_' + cred] = (value !== "");
|
||||
if (value == '__PWRD__') {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
|
||||
}
|
||||
node.credentials[cred] = value;
|
||||
if (value != node.credentials._[cred]) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare all of the editor dialog fields
|
||||
* @param node - the node being edited
|
||||
* @param definition - the node definition
|
||||
* @param prefix - the prefix to use in the input element ids (node-input|node-config-input)
|
||||
*/
|
||||
function prepareEditDialog(node,definition,prefix) {
|
||||
for (var d in definition.defaults) {
|
||||
if (definition.defaults.hasOwnProperty(d)) {
|
||||
if (definition.defaults[d].type) {
|
||||
if (definition.defaults[d].exclusive) {
|
||||
prepareConfigNodeButton(node,d,definition.defaults[d].type);
|
||||
} else {
|
||||
prepareConfigNodeSelect(node,d,definition.defaults[d].type);
|
||||
}
|
||||
} else {
|
||||
preparePropertyEditor(node,d,prefix);
|
||||
}
|
||||
attachPropertyChangeHandler(node,definition.defaults,d,prefix);
|
||||
}
|
||||
}
|
||||
var completePrepare = function() {
|
||||
if (definition.oneditprepare) {
|
||||
definition.oneditprepare.call(node);
|
||||
}
|
||||
for (var d in definition.defaults) {
|
||||
if (definition.defaults.hasOwnProperty(d)) {
|
||||
$("#"+prefix+"-"+d).change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.credentials) {
|
||||
if (node.credentials) {
|
||||
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
|
||||
completePrepare();
|
||||
} else {
|
||||
$.getJSON(getCredentialsURL(node.type, node.id), function (data) {
|
||||
node.credentials = data;
|
||||
node.credentials._ = $.extend(true,{},data);
|
||||
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
|
||||
completePrepare();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
completePrepare();
|
||||
}
|
||||
}
|
||||
|
||||
function showEditDialog(node) {
|
||||
editing_node = node;
|
||||
RED.view.state(RED.state.EDITING);
|
||||
var type = node.type;
|
||||
if (node.type.substring(0,8) == "subflow:") {
|
||||
type = "subflow";
|
||||
var id = editing_node.type.substring(8);
|
||||
var buttons = $( "#dialog" ).dialog("option","buttons");
|
||||
buttons.unshift({
|
||||
class: 'leftButton',
|
||||
text: "Edit flow",
|
||||
click: function() {
|
||||
RED.workspaces.show(id);
|
||||
$("#node-dialog-ok").click();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).dialog("option","buttons",buttons);
|
||||
}
|
||||
$("#dialog-form").html($("script[data-template-name='"+type+"']").html());
|
||||
$('<input type="text" style="display: none;" />').appendTo("#dialog-form");
|
||||
prepareEditDialog(node,node._def,"node-input");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$( "#dialog" ).dialog("option","title","Edit "+type+" node").dialog( "open" );
|
||||
}
|
||||
|
||||
function showEditConfigNodeDialog(name,type,id) {
|
||||
var adding = (id == "_ADD_");
|
||||
var node_def = RED.nodes.getType(type);
|
||||
|
||||
var configNode = RED.nodes.node(id);
|
||||
if (configNode == null) {
|
||||
configNode = {
|
||||
id: (1+Math.random()*4294967295).toString(16),
|
||||
_def: node_def,
|
||||
type: type
|
||||
}
|
||||
for (var d in node_def.defaults) {
|
||||
if (node_def.defaults[d].value) {
|
||||
configNode[d] = node_def.defaults[d].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#dialog-config-form").html($("script[data-template-name='"+type+"']").html());
|
||||
prepareEditDialog(configNode,node_def,"node-config-input");
|
||||
|
||||
var buttons = $( "#node-config-dialog" ).dialog("option","buttons");
|
||||
if (adding) {
|
||||
if (buttons.length == 3) {
|
||||
buttons = buttons.splice(1);
|
||||
}
|
||||
buttons[0].text = "Add";
|
||||
$("#node-config-dialog-user-count").html("").hide();
|
||||
} else {
|
||||
if (buttons.length == 2) {
|
||||
buttons.unshift({
|
||||
class: 'leftButton',
|
||||
text: "Delete",
|
||||
click: function() {
|
||||
var configProperty = $(this).dialog('option','node-property');
|
||||
var configId = $(this).dialog('option','node-id');
|
||||
var configType = $(this).dialog('option','node-type');
|
||||
var configNode = RED.nodes.node(configId);
|
||||
var configTypeDef = RED.nodes.getType(configType);
|
||||
|
||||
if (configTypeDef.ondelete) {
|
||||
configTypeDef.ondelete.call(RED.nodes.node(configId));
|
||||
}
|
||||
if (configTypeDef.oneditdelete) {
|
||||
configTypeDef.oneditdelete.call(RED.nodes.node(configId));
|
||||
}
|
||||
RED.nodes.remove(configId);
|
||||
for (var i=0;i<configNode.users.length;i++) {
|
||||
var user = configNode.users[i];
|
||||
for (var d in user._def.defaults) {
|
||||
if (user._def.defaults.hasOwnProperty(d) && user[d] == configId) {
|
||||
user[d] = "";
|
||||
}
|
||||
}
|
||||
validateNode(user);
|
||||
}
|
||||
updateConfigNodeSelect(configProperty,configType,"");
|
||||
RED.nodes.dirty(true);
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
buttons[1].text = "Update";
|
||||
$("#node-config-dialog-user-count").html(configNode.users.length+" node"+(configNode.users.length==1?" uses":"s use")+" this config").show();
|
||||
}
|
||||
$( "#node-config-dialog" ).dialog("option","buttons",buttons);
|
||||
|
||||
$( "#node-config-dialog" )
|
||||
.dialog("option","node-adding",adding)
|
||||
.dialog("option","node-property",name)
|
||||
.dialog("option","node-id",configNode.id)
|
||||
.dialog("option","node-type",type)
|
||||
.dialog("option","title",(adding?"Add new ":"Edit ")+type+" config node")
|
||||
.dialog( "open" );
|
||||
}
|
||||
|
||||
function updateConfigNodeSelect(name,type,value) {
|
||||
var button = $("#node-input-edit-"+name);
|
||||
if (button.length) {
|
||||
if (value) {
|
||||
button.text("edit");
|
||||
} else {
|
||||
button.text("add");
|
||||
}
|
||||
$("#node-input-"+name).val(value);
|
||||
} else {
|
||||
|
||||
var select = $("#node-input-"+name);
|
||||
var node_def = RED.nodes.getType(type);
|
||||
select.children().remove();
|
||||
RED.nodes.eachConfig(function(config) {
|
||||
if (config.type == type) {
|
||||
var label = "";
|
||||
if (typeof node_def.label == "function") {
|
||||
label = node_def.label.call(config);
|
||||
} else {
|
||||
label = node_def.label;
|
||||
}
|
||||
select.append('<option value="'+config.id+'"'+(value==config.id?" selected":"")+'>'+label+'</option>');
|
||||
}
|
||||
});
|
||||
|
||||
select.append('<option value="_ADD_"'+(value===""?" selected":"")+'>Add new '+type+'...</option>');
|
||||
window.setTimeout(function() { select.change();},50);
|
||||
}
|
||||
}
|
||||
|
||||
$( "#node-config-dialog" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
dialogClass: "ui-dialog-no-close",
|
||||
minWidth: 500,
|
||||
width: 'auto',
|
||||
closeOnEscape: false,
|
||||
buttons: [
|
||||
{
|
||||
id: "node-config-dialog-ok",
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
var configProperty = $(this).dialog('option','node-property');
|
||||
var configId = $(this).dialog('option','node-id');
|
||||
var configType = $(this).dialog('option','node-type');
|
||||
var configAdding = $(this).dialog('option','node-adding');
|
||||
var configTypeDef = RED.nodes.getType(configType);
|
||||
var configNode;
|
||||
var d;
|
||||
var input;
|
||||
|
||||
if (configAdding) {
|
||||
configNode = {type:configType,id:configId,users:[]};
|
||||
for (d in configTypeDef.defaults) {
|
||||
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
||||
input = $("#node-config-input-"+d);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
configNode[d] = input.prop('checked');
|
||||
} else {
|
||||
configNode[d] = input.val();
|
||||
}
|
||||
}
|
||||
}
|
||||
configNode.label = configTypeDef.label;
|
||||
configNode._def = configTypeDef;
|
||||
RED.nodes.add(configNode);
|
||||
updateConfigNodeSelect(configProperty,configType,configNode.id);
|
||||
} else {
|
||||
configNode = RED.nodes.node(configId);
|
||||
for (d in configTypeDef.defaults) {
|
||||
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
||||
input = $("#node-config-input-"+d);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
configNode[d] = input.prop('checked');
|
||||
} else {
|
||||
configNode[d] = input.val();
|
||||
}
|
||||
}
|
||||
}
|
||||
updateConfigNodeSelect(configProperty,configType,configId);
|
||||
}
|
||||
if (configTypeDef.credentials) {
|
||||
updateNodeCredentials(configNode,configTypeDef.credentials,"node-config-input");
|
||||
}
|
||||
if (configTypeDef.oneditsave) {
|
||||
configTypeDef.oneditsave.call(RED.nodes.node(configId));
|
||||
}
|
||||
validateNode(configNode);
|
||||
for (var i=0;i<configNode.users.length;i++) {
|
||||
var user = configNode.users[i];
|
||||
validateNode(user);
|
||||
}
|
||||
|
||||
RED.nodes.dirty(true);
|
||||
$(this).dialog("close");
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-config-dialog-cancel",
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
var configType = $(this).dialog('option','node-type');
|
||||
var configId = $(this).dialog('option','node-id');
|
||||
var configAdding = $(this).dialog('option','node-adding');
|
||||
var configTypeDef = RED.nodes.getType(configType);
|
||||
|
||||
if (configTypeDef.oneditcancel) {
|
||||
// TODO: what to pass as this to call
|
||||
if (configTypeDef.oneditcancel) {
|
||||
var cn = RED.nodes.node(configId);
|
||||
if (cn) {
|
||||
configTypeDef.oneditcancel.call(cn,false);
|
||||
} else {
|
||||
configTypeDef.oneditcancel.call({id:configId},true);
|
||||
}
|
||||
}
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
resize: function(e,ui) {
|
||||
},
|
||||
open: function(e) {
|
||||
var minWidth = $(this).dialog('option','minWidth');
|
||||
if ($(this).outerWidth() < minWidth) {
|
||||
$(this).dialog('option','width',minWidth);
|
||||
}
|
||||
if (RED.view.state() != RED.state.EDITING) {
|
||||
RED.keyboard.disable();
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
$(this).dialog('option','width','auto');
|
||||
$(this).dialog('option','height','auto');
|
||||
$("#dialog-config-form").html("");
|
||||
if (RED.view.state() != RED.state.EDITING) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
$( "#subflow-dialog" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
dialogClass: "ui-dialog-no-close",
|
||||
closeOnEscape: false,
|
||||
minWidth: 500,
|
||||
width: 'auto',
|
||||
buttons: [
|
||||
{
|
||||
id: "subflow-dialog-ok",
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
if (editing_node) {
|
||||
var i;
|
||||
var changes = {};
|
||||
var changed = false;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
|
||||
var newName = $("#subflow-input-name").val();
|
||||
|
||||
if (newName != editing_node.name) {
|
||||
changes['name'] = editing_node.name;
|
||||
editing_node.name = newName;
|
||||
changed = true;
|
||||
$("#menu-item-workspace-menu-"+editing_node.id.replace(".","-")).text("Subflow: "+newName);
|
||||
}
|
||||
|
||||
RED.palette.refresh();
|
||||
|
||||
if (changed) {
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+editing_node.id) {
|
||||
n.changed = true;
|
||||
updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
var wasChanged = editing_node.changed;
|
||||
editing_node.changed = true;
|
||||
RED.nodes.dirty(true);
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:editing_node,
|
||||
changes:changes,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged
|
||||
};
|
||||
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
editing_node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "subflow-dialog-cancel",
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
editing_node = null;
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
var minWidth = $(this).dialog('option','minWidth');
|
||||
if ($(this).outerWidth() < minWidth) {
|
||||
$(this).dialog('option','width',minWidth);
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
|
||||
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
|
||||
RED.view.state(RED.state.DEFAULT);
|
||||
}
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
editing_node = null;
|
||||
}
|
||||
});
|
||||
$("#subflow-dialog form" ).submit(function(e) { e.preventDefault();});
|
||||
|
||||
|
||||
function showEditSubflowDialog(subflow) {
|
||||
editing_node = subflow;
|
||||
RED.view.state(RED.state.EDITING);
|
||||
$("#subflow-input-name").val(subflow.name);
|
||||
var userCount = 0;
|
||||
var subflowType = "subflow:"+editing_node.id;
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type === subflowType) {
|
||||
userCount++;
|
||||
}
|
||||
});
|
||||
|
||||
$("#subflow-dialog-user-count").html("There "+(userCount==1?"is":"are")+" "+userCount+" instance"+(userCount==1?" ":"s")+" of this subflow").show();
|
||||
$("#subflow-dialog").dialog("option","title","Edit flow "+subflow.name).dialog( "open" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
edit: showEditDialog,
|
||||
editConfig: showEditConfigNodeDialog,
|
||||
editSubflow: showEditSubflowDialog,
|
||||
validateNode: validateNode,
|
||||
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
||||
|
||||
createEditor: function(options) {
|
||||
var editor = ace.edit(options.id);
|
||||
editor.setTheme("ace/theme/tomorrow");
|
||||
var session = editor.getSession();
|
||||
if (options.mode) {
|
||||
session.setMode(options.mode);
|
||||
}
|
||||
if (options.foldStyle) {
|
||||
session.setFoldStyle(options.foldStyle);
|
||||
} else {
|
||||
session.setFoldStyle('markbeginend');
|
||||
}
|
||||
if (options.options) {
|
||||
editor.setOptions(options.options);
|
||||
} else {
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion:true,
|
||||
enableSnippets:true
|
||||
});
|
||||
}
|
||||
editor.$blockScrolling = Infinity;
|
||||
if (options.value) {
|
||||
session.setValue(options.value,-1);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
})();
|
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>Select all nodes</td></tr>'+
|
||||
'<tr><td><span class="help-key">Shift</span> + <span class="help-key">Click</span></td><td>Select all connected nodes</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Click</span></td><td>Add/remove node from selection</td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>Delete selected nodes or link</td></tr>'+
|
||||
'<tr><td> </td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">i</span></td><td>Import nodes</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">e</span></td><td>Export selected nodes</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>Toggle sidebar</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>Delete selected nodes or link</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">c</span></td><td>Copy selected nodes</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">x</span></td><td>Cut selected nodes</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">v</span></td><td>Paste nodes</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
|
||||
}
|
||||
|
||||
})();
|
416
editor/js/ui/library.js
Normal file
@@ -0,0 +1,416 @@
|
||||
/**
|
||||
* 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.library = (function() {
|
||||
|
||||
|
||||
function loadFlowLibrary() {
|
||||
$.getJSON("library/flows",function(data) {
|
||||
//console.log(data);
|
||||
|
||||
var buildMenu = function(data,root) {
|
||||
var i;
|
||||
var li;
|
||||
var a;
|
||||
var ul = document.createElement("ul");
|
||||
ul.id = "menu-item-import-library-submenu";
|
||||
ul.className = "dropdown-menu";
|
||||
if (data.d) {
|
||||
for (i in data.d) {
|
||||
if (data.d.hasOwnProperty(i)) {
|
||||
li = document.createElement("li");
|
||||
li.className = "dropdown-submenu pull-left";
|
||||
a = document.createElement("a");
|
||||
a.href="#";
|
||||
a.innerHTML = i;
|
||||
li.appendChild(a);
|
||||
li.appendChild(buildMenu(data.d[i],root+(root!==""?"/":"")+i));
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.f) {
|
||||
for (i in data.f) {
|
||||
if (data.f.hasOwnProperty(i)) {
|
||||
li = document.createElement("li");
|
||||
a = document.createElement("a");
|
||||
a.href="#";
|
||||
a.innerHTML = data.f[i];
|
||||
a.flowName = root+(root!==""?"/":"")+data.f[i];
|
||||
a.onclick = function() {
|
||||
$.get('library/flows/'+this.flowName, function(data) {
|
||||
RED.view.importNodes(data);
|
||||
});
|
||||
};
|
||||
li.appendChild(a);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ul;
|
||||
};
|
||||
var menu = buildMenu(data,"");
|
||||
//TODO: need an api in RED.menu for this
|
||||
$("#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;
|
||||
for (var i=0;i<data.length;i++) {
|
||||
var v = data[i];
|
||||
if (typeof v === "string") {
|
||||
// directory
|
||||
li = buildFileListItem(v);
|
||||
li.onclick = (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) {
|
||||
$(this).parent().nextAll().remove();
|
||||
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
var bc = $("#node-dialog-library-breadcrumbs");
|
||||
$(".active",bc).removeClass("active");
|
||||
bc.append(bcli);
|
||||
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
||||
});
|
||||
}
|
||||
})();
|
||||
li.innerHTML = '<i class="fa fa-folder"></i> '+v+"</i>";
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
// file
|
||||
li = buildFileListItem(v);
|
||||
li.innerHTML = v.name;
|
||||
li.onclick = (function() {
|
||||
var item = v;
|
||||
return function(e) {
|
||||
$(".list-selected",ul).removeClass("list-selected");
|
||||
$(this).addClass("list-selected");
|
||||
$.get("library/"+options.url+root+item.fn, function(data) {
|
||||
selectedLibraryItem = item;
|
||||
libraryEditor.setValue(data,-1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
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>'+
|
||||
'<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>'+
|
||||
'</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.setValue('',-1);
|
||||
|
||||
$.getJSON("library/"+options.url,function(data) {
|
||||
$("#node-select-library").append(buildFileList("/",data));
|
||||
$("#node-dialog-library-breadcrumbs a").click(function(e) {
|
||||
$(this).parent().nextAll().remove();
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList("/",data));
|
||||
e.stopPropagation();
|
||||
});
|
||||
$( "#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,"");
|
||||
|
||||
//var buildPathList = function(data,root) {
|
||||
// var paths = [];
|
||||
// if (data.d) {
|
||||
// for (var i in data.d) {
|
||||
// var dn = root+(root==""?"":"/")+i;
|
||||
// var d = {
|
||||
// label:dn,
|
||||
// files:[]
|
||||
// };
|
||||
// for (var f in data.d[i].f) {
|
||||
// d.files.push(data.d[i].f[f].fn.split("/").slice(-1)[0]);
|
||||
// }
|
||||
// paths.push(d);
|
||||
// paths = paths.concat(buildPathList(data.d[i],root+(root==""?"":"/")+i));
|
||||
// }
|
||||
// }
|
||||
// return paths;
|
||||
//};
|
||||
$("#node-dialog-library-save-folder").attr("value","");
|
||||
|
||||
var filename = name.replace(/[^\w-]/g,"-");
|
||||
if (filename === "") {
|
||||
filename = "unnamed-"+options.type;
|
||||
}
|
||||
$("#node-dialog-library-save-filename").attr("value",filename+".js");
|
||||
|
||||
//var paths = buildPathList(libraryData,"");
|
||||
//$("#node-dialog-library-save-folder").autocomplete({
|
||||
// minLength: 0,
|
||||
// source: paths,
|
||||
// select: function( event, ui ) {
|
||||
// $("#node-dialog-library-save-filename").autocomplete({
|
||||
// minLength: 0,
|
||||
// source: ui.item.files
|
||||
// });
|
||||
// }
|
||||
//});
|
||||
|
||||
$( "#node-dialog-library-save" ).dialog( "open" );
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
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",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 800,
|
||||
height: 450,
|
||||
buttons: [
|
||||
{
|
||||
text: "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.setValue(libraryEditor.getValue(),-1);
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
var form = $("form",this);
|
||||
form.height(form.parent().height()-30);
|
||||
$("#node-select-library-text").height("100%");
|
||||
$(".form-row:last-child",form).children().height(form.height()-60);
|
||||
},
|
||||
resize: function(e) {
|
||||
var form = $("form",this);
|
||||
form.height(form.parent().height()-30);
|
||||
$(".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;
|
||||
}
|
||||
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");
|
||||
return;
|
||||
}
|
||||
var fullpath = pathname+(pathname===""?"":"/")+filename;
|
||||
if (!overwrite) {
|
||||
//var pathnameParts = pathname.split("/");
|
||||
//var exists = false;
|
||||
//var ds = libraryData;
|
||||
//for (var pnp in pathnameParts) {
|
||||
// if (ds.d && pathnameParts[pnp] in ds.d) {
|
||||
// ds = ds.d[pathnameParts[pnp]];
|
||||
// } else {
|
||||
// ds = null;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//if (ds && ds.f) {
|
||||
// for (var f in ds.f) {
|
||||
// if (ds.f[f].fn == fullpath) {
|
||||
// exists = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//if (exists) {
|
||||
// $("#node-dialog-library-save-type").html(options.type);
|
||||
// $("#node-dialog-library-save-name").html(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") {
|
||||
data.name = name;
|
||||
} else {
|
||||
data[field] = $("#node-input-"+field).val();
|
||||
}
|
||||
}
|
||||
|
||||
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("Saved "+options.type,"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.notify("Saved failed: "+xhr.responseJSON.message,"error");
|
||||
});
|
||||
}
|
||||
$( "#node-dialog-library-save-confirm" ).dialog({
|
||||
title: "Save to library",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
saveToLibrary(true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
$( "#node-dialog-library-save" ).dialog({
|
||||
title: "Save to library",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
saveToLibrary(false);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function exportFlow() {
|
||||
//TODO: don't rely on the main dialog
|
||||
var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes);
|
||||
$("#dialog-form").html($("script[data-template-name='export-library-dialog']").html());
|
||||
$("#node-input-filename").attr('nodes',JSON.stringify(nns));
|
||||
$( "#dialog" ).dialog("option","title","Export nodes to library").dialog( "open" );
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.view.on("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();
|
||||
}
|
||||
},
|
||||
create: createUI,
|
||||
loadFlowLibrary: loadFlowLibrary,
|
||||
|
||||
export: exportFlow
|
||||
}
|
||||
})();
|
||||
|
||||
|
244
editor/js/ui/menu.js
Normal file
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Copyright 2014 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.menu = (function() {
|
||||
|
||||
var menuItems = {};
|
||||
|
||||
function createMenuItem(opt) {
|
||||
var item;
|
||||
|
||||
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");
|
||||
opt.onselect.call(opt, true);
|
||||
} else if (savedStateActive === false) {
|
||||
link.removeClass("active");
|
||||
opt.onselect.call(opt, false);
|
||||
} else if (opt.hasOwnProperty("selected")) {
|
||||
if (opt.selected) {
|
||||
link.addClass("active");
|
||||
} else {
|
||||
link.removeClass("active");
|
||||
}
|
||||
opt.onselect.call(opt, opt.selected);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt === null) {
|
||||
item = $('<li class="divider"></li>');
|
||||
} else {
|
||||
item = $('<li></li>');
|
||||
|
||||
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)) {
|
||||
linkContent += '<img src="'+opt.icon+'"/> ';
|
||||
} else {
|
||||
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;
|
||||
|
||||
if (opt.onselect) {
|
||||
link.click(function() {
|
||||
if ($(this).parent().hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
if (opt.toggle) {
|
||||
var selected = isSelected(opt.id);
|
||||
if (typeof opt.toggle === "string") {
|
||||
if (!selected) {
|
||||
for (var m in menuItems) {
|
||||
if (menuItems.hasOwnProperty(m)) {
|
||||
var mi = menuItems[m];
|
||||
if (mi.id != opt.id && opt.toggle == mi.toggle) {
|
||||
setSelected(mi.id,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
setSelected(opt.id,true);
|
||||
}
|
||||
} else {
|
||||
setSelected(opt.id, !selected);
|
||||
}
|
||||
} else {
|
||||
opt.onselect.call(opt);
|
||||
}
|
||||
});
|
||||
setInitialState();
|
||||
} else if (opt.href) {
|
||||
link.attr("target","_blank").attr("href",opt.href);
|
||||
} else if (!opt.options) {
|
||||
item.addClass("disabled");
|
||||
link.click(function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
if (opt.options) {
|
||||
item.addClass("dropdown-submenu pull-left");
|
||||
var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item);
|
||||
|
||||
for (var i=0;i<opt.options.length;i++) {
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return item;
|
||||
|
||||
}
|
||||
function createMenu(options) {
|
||||
|
||||
var button = $("#"+options.id);
|
||||
|
||||
//button.click(function(event) {
|
||||
// $("#"+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];
|
||||
if (opt !== null || !lastAddedSeparator) {
|
||||
var li = createMenuItem(opt);
|
||||
if (li) {
|
||||
li.appendTo(topMenu);
|
||||
lastAddedSeparator = (opt === null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSavedStateActive(id) {
|
||||
return RED.settings.get("menu-" + id);
|
||||
}
|
||||
|
||||
function isSelected(id) {
|
||||
return $("#" + id).hasClass("active");
|
||||
}
|
||||
|
||||
function setSavedState(id, state) {
|
||||
RED.settings.set("menu-" + id, state);
|
||||
}
|
||||
|
||||
function setSelected(id,state) {
|
||||
if (isSelected(id) == state) {
|
||||
return;
|
||||
}
|
||||
var opt = menuItems[id];
|
||||
if (state) {
|
||||
$("#"+id).addClass("active");
|
||||
} else {
|
||||
$("#"+id).removeClass("active");
|
||||
}
|
||||
if (opt && opt.onselect) {
|
||||
opt.onselect.call(opt,state);
|
||||
}
|
||||
setSavedState(id, state);
|
||||
}
|
||||
|
||||
function setDisabled(id,state) {
|
||||
if (state) {
|
||||
$("#"+id).parent().addClass("disabled");
|
||||
} else {
|
||||
$("#"+id).parent().removeClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
function addItem(id,opt) {
|
||||
createMenuItem(opt).appendTo("#"+id+"-submenu");
|
||||
}
|
||||
function removeItem(id) {
|
||||
$("#"+id).parent().remove();
|
||||
}
|
||||
|
||||
function setAction(id,action) {
|
||||
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 {
|
||||
init: createMenu,
|
||||
setSelected: setSelected,
|
||||
isSelected: isSelected,
|
||||
setDisabled: setDisabled,
|
||||
addItem: addItem,
|
||||
removeItem: removeItem,
|
||||
setAction: setAction
|
||||
//TODO: add an api for replacing a submenu - see library.js:loadFlowLibrary
|
||||
}
|
||||
})();
|
@@ -13,17 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.notify = function() {
|
||||
RED.notify = (function() {
|
||||
var currentNotifications = [];
|
||||
var c = 0;
|
||||
return function(msg,type,fixed,timeout) {
|
||||
if (currentNotifications.length > 4) {
|
||||
var ll = currentNotifications.length;
|
||||
for (var i = 0;ll > 4 && i<currentNotifications.length;i+=1) {
|
||||
var n = currentNotifications[i];
|
||||
if (!n.fixed) {
|
||||
window.clearTimeout(n.timeoutid);
|
||||
n.close();
|
||||
var notifiction = currentNotifications[i];
|
||||
if (!notifiction.fixed) {
|
||||
window.clearTimeout(notifiction.timeoutid);
|
||||
notifiction.close();
|
||||
ll -= 1;
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ RED.notify = function() {
|
||||
n.innerHTML = msg;
|
||||
$("#notifications").append(n);
|
||||
$(n).slideDown(300);
|
||||
n.close = function() {
|
||||
n.close = (function() {
|
||||
var nn = n;
|
||||
return function() {
|
||||
currentNotifications.splice(currentNotifications.indexOf(nn),1);
|
||||
@@ -47,7 +47,7 @@ RED.notify = function() {
|
||||
nn.parentNode.removeChild(nn);
|
||||
});
|
||||
};
|
||||
}();
|
||||
})();
|
||||
if (!fixed) {
|
||||
n.timeoutid = window.setTimeout(n.close,timeout||3000);
|
||||
}
|
||||
@@ -55,5 +55,5 @@ RED.notify = function() {
|
||||
c+=1;
|
||||
return n;
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
310
editor/js/ui/palette.js
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* 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.palette = (function() {
|
||||
|
||||
var exclusion = ['config','unknown','deprecated'];
|
||||
var core = ['subflows', 'input', 'output', 'function', 'social', 'storage', 'analysis', 'advanced'];
|
||||
|
||||
function createCategoryContainer(category){
|
||||
var escapedCategory = category.replace(" ","_");
|
||||
var catDiv = $("#palette-container").append('<div id="palette-container-'+category+'" class="palette-category hide">'+
|
||||
'<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-caret-down"></i><span>'+category.replace("_"," ")+'</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>');
|
||||
|
||||
$("#palette-header-"+category).on('click', function(e) {
|
||||
$(this).next().slideToggle();
|
||||
$(this).children("i").toggleClass("expanded");
|
||||
});
|
||||
}
|
||||
|
||||
function setLabel(type, el,label) {
|
||||
var nodeWidth = 80;
|
||||
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+($("script[data-help-name|='"+type+"']").html()||"<p>no information available</p>").trim())
|
||||
.filter(function(n) {
|
||||
return this.nodeType == 1 || (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
|
||||
console.log("Error generating pop-over label for '"+type+"'.");
|
||||
console.log(err.toString());
|
||||
popOverContent = "<p><b>"+label+"</b></p><p>no information available</p>";
|
||||
}
|
||||
|
||||
|
||||
el.data('popover').options.content = 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) {
|
||||
createCategoryContainer(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(); };
|
||||
|
||||
$(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 help = '<div class="node-help">'+($("script[data-help-name|='"+d.type+"']").html()||"")+"</div>";
|
||||
$("#tab-info").html(help);
|
||||
});
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
appendTo: 'body',
|
||||
revert: true,
|
||||
revertDuration: 50,
|
||||
start: function() {RED.view.focus();}
|
||||
});
|
||||
|
||||
if (def.category == "subflows") {
|
||||
$(d).dblclick(function(e) {
|
||||
RED.workspaces.show(nt.substring(8));
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
setLabel(nt,$(d),label);
|
||||
|
||||
var categoryNode = $("#palette-container-"+category);
|
||||
if (categoryNode.find(".palette_node").length === 1) {
|
||||
if (!categoryNode.find("i").hasClass("expanded")) {
|
||||
categoryNode.find(".palette-content").slideToggle();
|
||||
categoryNode.find("i").toggleClass("expanded");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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_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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
$(".palette-spinner").show();
|
||||
if (RED.settings.paletteCategories) {
|
||||
RED.settings.paletteCategories.forEach(createCategoryContainer);
|
||||
} else {
|
||||
core.forEach(createCategoryContainer);
|
||||
}
|
||||
|
||||
$("#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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add:addNodeType,
|
||||
remove:removeNodeType,
|
||||
hide:hideNodeType,
|
||||
show:showNodeType,
|
||||
refresh:refreshNodeTypes
|
||||
};
|
||||
})();
|
@@ -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,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.sidebar = function() {
|
||||
RED.sidebar = (function() {
|
||||
|
||||
//$('#sidebar').tabs();
|
||||
var sidebar_tabs = RED.tabs.create({
|
||||
@@ -26,6 +26,7 @@ RED.sidebar = function() {
|
||||
$("#"+tab.id).remove();
|
||||
}
|
||||
});
|
||||
|
||||
function addTab(title,content,closeable) {
|
||||
$("#sidebar-content").append(content);
|
||||
$(content).hide();
|
||||
@@ -33,10 +34,11 @@ RED.sidebar = function() {
|
||||
//content.style.position = "absolute";
|
||||
//$('#sidebar').tabs("refresh");
|
||||
}
|
||||
|
||||
$('#btn-sidebar').click(function() {toggleSidebar();});
|
||||
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){toggleSidebar();d3.event.preventDefault();});
|
||||
|
||||
function removeTab(title) {
|
||||
sidebar_tabs.removeTab("tab-"+title);
|
||||
}
|
||||
|
||||
var sidebarSeparator = {};
|
||||
$("#sidebar-separator").draggable({
|
||||
axis: "x",
|
||||
@@ -49,18 +51,16 @@ RED.sidebar = function() {
|
||||
sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2;
|
||||
|
||||
|
||||
if (!btnSidebar.hasClass("active")) {
|
||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
||||
sidebarSeparator.opening = true;
|
||||
var newChartRight = 15;
|
||||
$("#sidebar").addClass("closing");
|
||||
$("#workspace").css("right",newChartRight);
|
||||
$("#chart-zoom-controls").css("right",newChartRight+20);
|
||||
$("#sidebar").width(0);
|
||||
toggleSidebar();
|
||||
RED.view.resize();
|
||||
RED.menu.setSelected("menu-item-sidebar",true);
|
||||
eventHandler.emit("resize");
|
||||
}
|
||||
|
||||
|
||||
sidebarSeparator.width = $("#sidebar").width();
|
||||
},
|
||||
drag: function(event,ui) {
|
||||
@@ -99,14 +99,12 @@ RED.sidebar = function() {
|
||||
$("#sidebar").width(newSidebarWidth);
|
||||
|
||||
sidebar_tabs.resize();
|
||||
RED.view.resize();
|
||||
|
||||
eventHandler.emit("resize");
|
||||
},
|
||||
stop:function(event,ui) {
|
||||
RED.view.resize();
|
||||
if (sidebarSeparator.closing) {
|
||||
$("#sidebar").removeClass("closing");
|
||||
toggleSidebar();
|
||||
RED.menu.setSelected("menu-item-sidebar",false);
|
||||
if ($("#sidebar").width() < 180) {
|
||||
$("#sidebar").width(180);
|
||||
$("#workspace").css("right",208);
|
||||
@@ -115,39 +113,65 @@ RED.sidebar = function() {
|
||||
}
|
||||
$("#sidebar-separator").css("left","auto");
|
||||
$("#sidebar-separator").css("right",($("#sidebar").width()+13)+"px");
|
||||
eventHandler.emit("resize");
|
||||
}
|
||||
});
|
||||
|
||||
var btnSidebar = $("#btn-sidebar");
|
||||
function toggleSidebar() {
|
||||
//if ($('#sidebar').tabs( "option", "active" ) === false) {
|
||||
// $('#sidebar').tabs( "option", "active",0);
|
||||
//}
|
||||
btnSidebar.toggleClass("active");
|
||||
|
||||
if (!btnSidebar.hasClass("active")) {
|
||||
function toggleSidebar(state) {
|
||||
if (!state) {
|
||||
$("#main-container").addClass("sidebar-closed");
|
||||
} else {
|
||||
$("#main-container").removeClass("sidebar-closed");
|
||||
sidebar_tabs.resize();
|
||||
}
|
||||
eventHandler.emit("resize");
|
||||
}
|
||||
toggleSidebar();
|
||||
|
||||
function showSidebar(id) {
|
||||
if (!$("#btn-sidebar").hasClass("active")) {
|
||||
toggleSidebar();
|
||||
if (id) {
|
||||
sidebar_tabs.activateTab("tab-"+id);
|
||||
}
|
||||
sidebar_tabs.activateTab("tab-"+id);
|
||||
}
|
||||
|
||||
function containsTab(id) {
|
||||
return sidebar_tabs.contains("tab-"+id);
|
||||
}
|
||||
|
||||
return {
|
||||
addTab: addTab,
|
||||
show: showSidebar,
|
||||
containsTab: containsTab
|
||||
function init () {
|
||||
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();
|
||||
// hide info bar at start if screen rather narrow...
|
||||
if ($(window).width() < 600) { toggleSidebar(); }
|
||||
}
|
||||
|
||||
}();
|
||||
var eventHandler = (function() {
|
||||
var handlers = {};
|
||||
|
||||
return {
|
||||
on: function(evt,func) {
|
||||
handlers[evt] = handlers[evt]||[];
|
||||
handlers[evt].push(func);
|
||||
},
|
||||
emit: function(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
handlers[evt][i](arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
init: init,
|
||||
addTab: addTab,
|
||||
removeTab: removeTab,
|
||||
show: showSidebar,
|
||||
containsTab: containsTab,
|
||||
toggleSidebar: toggleSidebar,
|
||||
on: eventHandler.on
|
||||
}
|
||||
|
||||
})();
|
402
editor/js/ui/subflow.js
Normal file
@@ -0,0 +1,402 @@
|
||||
/**
|
||||
* 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) {
|
||||
var pos = {x:70,y:70};
|
||||
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());
|
||||
var position = findAvailableSubflowIOPosition(subflow);
|
||||
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;
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+subflow.id) {
|
||||
n.changed = true;
|
||||
n.inputs = subflow.in.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
inputCount: oldInCount
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
$("#workspace-subflow-add-input").toggleClass("disabled",true);
|
||||
RED.view.select();
|
||||
}
|
||||
|
||||
function addSubflowOutput(id) {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var position = findAvailableSubflowIOPosition(subflow);
|
||||
|
||||
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;
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+subflow.id) {
|
||||
n.changed = true;
|
||||
n.outputs = subflow.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
outputCount: oldOutCount
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
}
|
||||
|
||||
function init() {
|
||||
$("#workspace-subflow-edit").click(function(event) {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||
event.preventDefault();
|
||||
});
|
||||
$("#workspace-subflow-add-input").click(function(event) {
|
||||
event.preventDefault();
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
addSubflowInput();
|
||||
});
|
||||
$("#workspace-subflow-add-output").click(function(event) {
|
||||
event.preventDefault();
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
addSubflowOutput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-delete").click(function(event) {
|
||||
event.preventDefault();
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var startDirty = RED.nodes.dirty();
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+getSubflow().id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
if (n.z == getSubflow().id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
for (var i=0;i<removedNodes.length;i++) {
|
||||
var rmlinks = RED.nodes.remove(removedNodes[i].id);
|
||||
removedLinks = removedLinks.concat(rmlinks);
|
||||
}
|
||||
|
||||
var activeSubflow = getSubflow();
|
||||
|
||||
RED.nodes.removeSubflow(activeSubflow);
|
||||
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflow: activeSubflow,
|
||||
dirty:startDirty
|
||||
});
|
||||
|
||||
RED.workspaces.remove(activeSubflow);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
RED.view.on("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,
|
||||
in: [],
|
||||
out: []
|
||||
};
|
||||
RED.nodes.addSubflow(subflow);
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
subflow: subflow,
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.workspaces.show(subflowId);
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes) {
|
||||
RED.notify("<strong>Cannot create subflow</strong>: no nodes selected","error");
|
||||
return;
|
||||
}
|
||||
var i;
|
||||
var nodes = {};
|
||||
var new_links = [];
|
||||
var removedLinks = [];
|
||||
|
||||
var candidateInputs = [];
|
||||
var candidateOutputs = [];
|
||||
|
||||
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);
|
||||
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 (candidateInputs.length > 1) {
|
||||
RED.notify("<strong>Cannot create subflow</strong>: multiple inputs to selection","error");
|
||||
return;
|
||||
}
|
||||
//if (candidateInputs.length == 0) {
|
||||
// RED.notify("<strong>Cannot create subflow</strong>: no input to selection","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,
|
||||
in: candidateInputs.map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:v.target.x-(v.target.w/2)-80,
|
||||
y:v.target.y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:v.target.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,
|
||||
|
||||
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
|
||||
}
|
||||
})();
|
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.sidebar.config = function() {
|
||||
RED.sidebar.config = (function() {
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.id = "tab-config";
|
||||
@@ -23,19 +23,19 @@ RED.sidebar.config = function() {
|
||||
|
||||
var list = $("<ul>",{class:"tab-config-list"}).appendTo(content);
|
||||
|
||||
$("#btn-config-nodes").click(function(){
|
||||
function show() {
|
||||
if (!RED.sidebar.containsTab("config")) {
|
||||
RED.sidebar.addTab("config",content,true);
|
||||
}
|
||||
refresh();
|
||||
RED.sidebar.show("config");
|
||||
});
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
list.empty();
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
var li = list.find("#tab-config-list-type-"+node.type);
|
||||
if (li.length == 0) {
|
||||
if (li.length === 0) {
|
||||
li = $("<li>",{id:"tab-config-list-type-"+node.type}).appendTo(list);
|
||||
$('<div class="tab-config-list-type">'+node.type+'</div>').appendTo(li);
|
||||
}
|
||||
@@ -78,6 +78,7 @@ RED.sidebar.config = function() {
|
||||
});
|
||||
}
|
||||
return {
|
||||
show:show,
|
||||
refresh:refresh
|
||||
}
|
||||
}();
|
||||
})();
|
191
editor/js/ui/tab-info.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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.id = "tab-info";
|
||||
content.style.paddingTop = "4px";
|
||||
content.style.paddingLeft = "4px";
|
||||
content.style.paddingRight = "4px";
|
||||
|
||||
var propertiesExpanded = false;
|
||||
|
||||
function show() {
|
||||
if (!RED.sidebar.containsTab("info")) {
|
||||
RED.sidebar.addTab("info",content,false);
|
||||
}
|
||||
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">Node</td></tr>';
|
||||
if (node.type != "subflow" && node.name) {
|
||||
table += "<tr><td>Name</td><td> "+node.name+"</td></tr>";
|
||||
}
|
||||
table += "<tr><td>Type</td><td> "+node.type+"</td></tr>";
|
||||
table += "<tr><td>ID</td><td> "+node.id+"</td></tr>";
|
||||
|
||||
var m = /^subflow(:(.+))?$/.exec(node.type);
|
||||
if (m) {
|
||||
var subflowNode;
|
||||
if (m[2]) {
|
||||
subflowNode = RED.nodes.subflow(m[2]);
|
||||
} else {
|
||||
subflowNode = node;
|
||||
}
|
||||
|
||||
table += '<tr class="blank"><td colspan="2">Subflow</td></tr>';
|
||||
|
||||
var userCount = 0;
|
||||
var subflowType = "subflow:"+subflowNode.id;
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type === subflowType) {
|
||||
userCount++;
|
||||
}
|
||||
});
|
||||
table += "<tr><td>name</td><td>"+subflowNode.name+"</td></tr>";
|
||||
table += "<tr><td>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> 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;">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 += " ... "+node[n].length+" items<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 (node.type != "comment") {
|
||||
var helpText = $("script[data-help-name|='"+node.type+"']").html()||"";
|
||||
table += '<div class="node-help">'+helpText+"</div>";
|
||||
}
|
||||
|
||||
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>';
|
||||
}
|
||||
|
||||
$("#tab-info").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() {
|
||||
$("#tab-info").html("");
|
||||
}
|
||||
|
||||
RED.view.on("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 {
|
||||
show: show,
|
||||
refresh:refresh,
|
||||
clear: clear
|
||||
}
|
||||
})();
|
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
|
||||
RED.tabs = function() {
|
||||
RED.tabs = (function() {
|
||||
|
||||
|
||||
function createTabs(options) {
|
||||
@@ -69,7 +69,7 @@ RED.tabs = function() {
|
||||
var li = ul.find("a[href='#"+id+"']").parent();
|
||||
if (li.hasClass("active")) {
|
||||
var tab = li.prev();
|
||||
if (tab.size() == 0) {
|
||||
if (tab.size() === 0) {
|
||||
tab = li.next();
|
||||
}
|
||||
activateTab(tab.find("a"));
|
||||
@@ -93,7 +93,7 @@ RED.tabs = function() {
|
||||
link.on("dblclick",onTabDblClick);
|
||||
if (tab.closeable) {
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
closeLink.html('<i class="icon-remove" />');
|
||||
closeLink.html('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.on("click",function(event) {
|
||||
removeTab(tab.id);
|
||||
@@ -116,6 +116,13 @@ RED.tabs = function() {
|
||||
},
|
||||
contains: function(id) {
|
||||
return ul.find("a[href='#"+id+"']").length > 0;
|
||||
},
|
||||
renameTab: function(id,label) {
|
||||
tabs[id].label = label;
|
||||
var tab = ul.find("a[href='#"+id+"']");
|
||||
tab.attr("title",label);
|
||||
tab.text(label);
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -124,4 +131,4 @@ RED.tabs = function() {
|
||||
return {
|
||||
create: createTabs
|
||||
}
|
||||
}();
|
||||
})();
|
@@ -15,7 +15,7 @@
|
||||
**/
|
||||
|
||||
RED.touch = RED.touch||{};
|
||||
RED.touch.radialMenu = function() {
|
||||
RED.touch.radialMenu = (function() {
|
||||
|
||||
|
||||
var touchMenu = null;
|
||||
@@ -60,7 +60,7 @@ RED.touch.radialMenu = function() {
|
||||
});
|
||||
|
||||
var menuOpts = [];
|
||||
function createMenuOpt(x,y,opt) {
|
||||
var createMenuOpt = function(x,y,opt) {
|
||||
opt.el = menu.append("div")
|
||||
.style({
|
||||
position: "absolute",
|
||||
@@ -74,11 +74,9 @@ RED.touch.radialMenu = function() {
|
||||
"text-align": "center",
|
||||
"line-height":"50px"
|
||||
});
|
||||
if (opt.icon) {
|
||||
opt.el.append("i").attr("class","icon "+opt.icon)
|
||||
} else {
|
||||
opt.el.html(opt.name);
|
||||
}
|
||||
|
||||
opt.el.html(opt.name);
|
||||
|
||||
if (opt.disabled) {
|
||||
opt.el.style({"border-color":"#ccc",color:"#ccc"});
|
||||
}
|
||||
@@ -112,7 +110,7 @@ RED.touch.radialMenu = function() {
|
||||
}
|
||||
|
||||
|
||||
function hide() {
|
||||
var hide = function() {
|
||||
isActive = false;
|
||||
activeOption = null;
|
||||
touchMenu.remove();
|
||||
@@ -182,5 +180,5 @@ RED.touch.radialMenu = function() {
|
||||
}
|
||||
}
|
||||
|
||||
}();
|
||||
})();
|
||||
|
1841
editor/js/ui/view.js
Normal file
270
editor/js/ui/workspaces.js
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 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='Sheet "+workspaceIndex+"']").size() !== 0);
|
||||
|
||||
ws = {type:"tab",id:tabId,label:"Sheet "+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);
|
||||
} else {
|
||||
$( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws);
|
||||
$( "#node-dialog-delete-workspace-name" ).text(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 = RED.tabs.create({
|
||||
id: "workspace-tabs",
|
||||
onchange: function(tab) {
|
||||
if (tab.type == "subflow") {
|
||||
$("#workspace-toolbar").show();
|
||||
} else {
|
||||
$("#workspace-toolbar").hide();
|
||||
}
|
||||
var event = {
|
||||
old: activeWorkspace
|
||||
}
|
||||
activeWorkspace = tab.id;
|
||||
event.workspace = activeWorkspace;
|
||||
|
||||
eventHandler.emit("change",event);
|
||||
},
|
||||
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(".","-"));
|
||||
}
|
||||
});
|
||||
|
||||
$("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();});
|
||||
$( "#node-dialog-rename-workspace" ).dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
title: "Rename sheet",
|
||||
buttons: [
|
||||
{
|
||||
class: 'leftButton',
|
||||
text: "Delete",
|
||||
click: function() {
|
||||
var workspace = $(this).dialog('option','workspace');
|
||||
$( this ).dialog( "close" );
|
||||
deleteWorkspace(workspace);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "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);
|
||||
$("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(label);
|
||||
// TODO: update entry in menu
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "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: "Confirm delete",
|
||||
buttons: [
|
||||
{
|
||||
text: "Ok",
|
||||
click: function() {
|
||||
var workspace = $(this).dialog('option','workspace');
|
||||
deleteWorkspace(workspace,true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function init() {
|
||||
$('#btn-workspace-add-tab').on("click",function(e) {addWorkspace(); e.preventDefault()});
|
||||
RED.sidebar.on("resize",workspace_tabs.resize);
|
||||
|
||||
RED.menu.setAction('menu-item-workspace-delete',function() {
|
||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: DRY
|
||||
var eventHandler = (function() {
|
||||
var handlers = {};
|
||||
|
||||
return {
|
||||
on: function(evt,func) {
|
||||
handlers[evt] = handlers[evt]||[];
|
||||
handlers[evt].push(func);
|
||||
},
|
||||
emit: function(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
handlers[evt][i](arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
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,
|
||||
on: eventHandler.on,
|
||||
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,label:"Subflow: "+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,"Subflow: "+sf.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
resize: function() {
|
||||
workspace_tabs.resize();
|
||||
}
|
||||
}
|
||||
})();
|
189
editor/js/user.js
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* 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.
|
||||
* 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.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 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>'+
|
||||
'</div>');
|
||||
|
||||
dialog.dialog({
|
||||
autoOpen: false,
|
||||
dialogClass: "ui-dialog-no-close",
|
||||
modal: true,
|
||||
closeOnEscape: false,
|
||||
width: 600,
|
||||
resizable: false,
|
||||
draggable: false
|
||||
});
|
||||
|
||||
$("#node-dialog-login-fields").empty();
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: "auth/login",
|
||||
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/>",{id:"rrr"+i,class:"form-row"});
|
||||
$('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row);
|
||||
var input = $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row);
|
||||
|
||||
if (i<data.prompts.length-1) {
|
||||
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="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)+'">Cancel</a>':'')+
|
||||
'<input type="submit" id="node-dialog-login-submit" style="width: auto;" tabIndex="'+(i+2)+'" value="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:"*"
|
||||
}
|
||||
for (var i=0;i<data.prompts.length;i++) {
|
||||
var field = data.prompts[i];
|
||||
body[field.id] = $("#node-dialog-login-"+field.id).val();
|
||||
}
|
||||
$.ajax({
|
||||
url:"auth/token",
|
||||
type: "POST",
|
||||
data: body
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.settings.set("auth-tokens",data);
|
||||
$("#node-dialog-login").dialog('destroy').remove();
|
||||
done();
|
||||
}).fail(function(jqXHR,textStatus,errorThrown) {
|
||||
RED.settings.remove("auth-tokens");
|
||||
$("#node-dialog-login-failed").show();
|
||||
}).always(function() {
|
||||
$("#node-dialog-login-submit").button("option","disabled",false);
|
||||
$(".login-spinner").hide();
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
if (opts.cancelable) {
|
||||
$("#node-dialog-login-cancel").button().click(function( event ) {
|
||||
$("#node-dialog-login").dialog('destroy').remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
dialog.dialog("open");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
$.ajax({
|
||||
url: "auth/revoke",
|
||||
type: "POST",
|
||||
data: {token:RED.settings.get("auth-tokens").access_token},
|
||||
success: function() {
|
||||
RED.settings.remove("auth-tokens");
|
||||
document.location.reload(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateUserMenu() {
|
||||
$("#usermenu-submenu li").remove();
|
||||
if (RED.settings.user.anonymous) {
|
||||
RED.menu.addItem("btn-usermenu",{
|
||||
id:"usermenu-item-login",
|
||||
label:"Login",
|
||||
onselect: function() {
|
||||
RED.user.login({cancelable:true},function() {
|
||||
RED.settings.load(function() {
|
||||
RED.notify("Logged in as "+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:"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;
|
||||
}
|
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;
|
||||
}
|
76
editor/sass/editor.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
.leftButton {
|
||||
margin-right: 200px !important;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
clear: both;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
.form-row label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
.form-row input {
|
||||
width:70%;
|
||||
}
|
||||
|
||||
input.input-append-left {
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
button.input-append-right {
|
||||
border-top-left-radius: 0px !important;
|
||||
border-bottom-left-radius: 0px !important;
|
||||
border-top-right-radius: 4px !important;
|
||||
border-bottom-right-radius: 4px !important;
|
||||
margin-left: -1px !important;
|
||||
padding-left: 4px !important;
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
|
||||
.form-tips {
|
||||
background: lightgoldenrodyellow;
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #999;
|
||||
max-width: 450px;
|
||||
}
|
||||
.form-tips code {
|
||||
border: none;
|
||||
padding: auto;
|
||||
}
|
||||
|
||||
.node-text-editor {
|
||||
border:1px solid #ccc;
|
||||
border-radius:5px;
|
||||
overflow: hidden;
|
||||
font-size: 14px !important;
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
231
editor/sass/flow.scss
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* 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: 2px;
|
||||
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;
|
||||
}
|
||||
|
||||
#workspace {
|
||||
@include component-border;
|
||||
}
|
||||
|
||||
.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: 2;
|
||||
}
|
||||
.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: 2;
|
||||
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: #ff7f0e !important;
|
||||
}
|
||||
.node_highlighted {
|
||||
stroke: #dd1616;
|
||||
stroke-width: 3;
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
||||
.node_hovered {
|
||||
}
|
||||
|
||||
.port_hovered {
|
||||
stroke: #ff7f0e;
|
||||
fill: #ff7f0e;
|
||||
}
|
||||
.subflowport {
|
||||
stroke-dasharray: 5,5;
|
||||
fill: #eee;
|
||||
stroke: #999;
|
||||
}
|
||||
|
||||
.drag_line {
|
||||
stroke: #ff7f0e;
|
||||
stroke-width: 5;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.drag_line_hidden {
|
||||
stroke: #ff7f0e;
|
||||
stroke-width: 0;
|
||||
pointer-events: none;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.link_line {
|
||||
stroke: #7f7f7f;
|
||||
stroke-width: 4;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.link_subflow {
|
||||
stroke: #bbb;
|
||||
stroke-dasharray: 10,5;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.link_outline {
|
||||
stroke: #fff;
|
||||
stroke-width: 6;
|
||||
cursor: crosshair;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
.link_background {
|
||||
stroke: #fff;
|
||||
opacity: 0;
|
||||
stroke-width: 25;
|
||||
cursor: crosshair;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
g.link_selected path.link_line {
|
||||
stroke: #ff7f0e;
|
||||
}
|
||||
g.link_unknown path.link_line {
|
||||
stroke: #f00;
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
||||
|
267
editor/sass/header.scss
Normal file
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: inline-block;
|
||||
margin: auto 15px;
|
||||
vertical-align: middle;
|
||||
clear: both;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
53
editor/sass/jquery.scss
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
/* 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;
|
||||
border: 1px solid #eee;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
box-shadow: 2px 2px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
.ui-dialog .ui-dialog-content {
|
||||
padding: 25px 25px 10px 25px;
|
||||
}
|
||||
.ui-dialog .ui-dialog-titlebar {
|
||||
padding: 10px;
|
||||
background: #f0f0f0;
|
||||
border: none;
|
||||
border-bottom: 2px solid #888;
|
||||
border-radius: 0;
|
||||
}
|
||||
.ui-corner-all {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.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;
|
||||
}
|
40
editor/sass/keyboard.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
#keyboard-help-dialog {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.keyboard-shortcuts {
|
||||
padding: 10px;
|
||||
}
|
||||
.keyboard-shortcuts td {
|
||||
padding: 7px 5px;
|
||||
margin-bottom: 10px;
|
||||
white-space: pre;
|
||||
}
|
||||
.keyboard-shortcuts td:first-child {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.help-key {
|
||||
border: 1px solid #ddd;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
background: #f6f6f6;
|
||||
font-family: Courier, monospace;
|
||||
box-shadow: #999 1px 1px 1px;
|
||||
}
|
||||
|
33
editor/sass/library.scss
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
#node-select-library {
|
||||
overflow: hidden;
|
||||
}
|
||||
#node-select-library ul {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
margin: 2px;
|
||||
}
|
||||
#node-select-library li {
|
||||
cursor: pointer;
|
||||
}
|
||||
#node-select-library li.list-selected {
|
||||
background: #eee;
|
||||
}
|
||||
#node-select-library li.list-hover {
|
||||
background: #ffffd0;
|
||||
}
|
29
editor/sass/mixins.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
@mixin disable-selection {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@mixin component-border {
|
||||
border: 1px solid #000;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
32
editor/sass/notifications.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
|
||||
.notification {
|
||||
position: absolute;
|
||||
}
|
||||
#notifications {
|
||||
z-index: 10000;
|
||||
width: 500px;
|
||||
margin-left: -250px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
}
|
||||
#notifications .alert {
|
||||
box-shadow: 0 0 1px 1px;
|
||||
margin-bottom: 5px;
|
||||
}
|
200
editor/sass/palette.scss
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
|
||||
#palette {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
bottom: 10px;
|
||||
left:10px;
|
||||
background: #f3f3f3;
|
||||
width: 170px;
|
||||
text-align: center;
|
||||
@include disable-selection;
|
||||
@include component-border;
|
||||
|
||||
}
|
||||
.palette-scroll {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 35px;
|
||||
left:0;
|
||||
padding: 5px;
|
||||
overflow-y: auto;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
.palette-spinner {
|
||||
padding-top: 40px;
|
||||
}
|
||||
#palette-search {
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
left:0;
|
||||
right:0;
|
||||
overflow: hidden;
|
||||
background: #f3f3f3;
|
||||
text-align: center;
|
||||
height: 35px;
|
||||
padding: 3px;
|
||||
border-top: 1px solid #999;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
#palette-search i.fa-search {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 4px;
|
||||
top: 10px;
|
||||
}
|
||||
#palette-search i.fa-times {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
#palette-search-clear {
|
||||
display: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#palette-search input {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
width: 100%;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
padding: 3px 17px;
|
||||
margin: 0px;
|
||||
height: 30px;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
#palette-search input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
|
||||
.palette-category {
|
||||
border: 1px solid #999;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.palette-content {
|
||||
background: #fff;
|
||||
border-top: 1px solid #aaa;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.palette-header {
|
||||
background: #f3f3f3;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 1px;
|
||||
}
|
||||
.palette-header i {
|
||||
margin: 3px 4px 3px 3px;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-moz-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
-webkit-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
}
|
||||
.palette-header i.expanded {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
}
|
||||
.palette-header span {
|
||||
clear: both;
|
||||
}
|
||||
.palette_label {
|
||||
margin: 4px 0 4px 28px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
.palette_label_right {
|
||||
margin: 4px 28px 4px 0;
|
||||
}
|
||||
|
||||
.palette_node {
|
||||
cursor:move;
|
||||
font-size:13px;
|
||||
background: #ddd;
|
||||
margin: 10px auto;
|
||||
height: 25px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #999;
|
||||
background-position: 5% 50%;
|
||||
background-repeat: no-repeat;
|
||||
width: 120px;
|
||||
background-size: contain;
|
||||
position: relative;
|
||||
}
|
||||
.palette_node:hover {
|
||||
border-color: #ff7f0e;
|
||||
background-color: #eee;
|
||||
}
|
||||
.palette_port {
|
||||
position: absolute;
|
||||
top:8px;
|
||||
left: -5px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
background:#d9d9d9;
|
||||
border-radius: 3px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.palette_port_output {
|
||||
left:auto;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.palette_node:hover .palette_port {
|
||||
border-color: #999;
|
||||
background-color: #eee;
|
||||
}
|
||||
.palette_icon_container {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top:0;
|
||||
bottom:0;
|
||||
left:0;
|
||||
width: 30px;
|
||||
border-right: 2px solid rgba(0,0,0,0.1);
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
}
|
||||
.palette_icon_container_right {
|
||||
left: auto;
|
||||
right: 0;
|
||||
border-right: none;
|
||||
border-left: 2px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
.palette_icon {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 100%;
|
||||
background-position: 50% 50%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
60
editor/sass/sidebar.scss
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
|
||||
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
width: 305px;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
@include component-border;
|
||||
}
|
||||
|
||||
#sidebar.closing {
|
||||
background: #eee;
|
||||
border-color: #900;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
#sidebar-content {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
bottom: 1px;
|
||||
left: 0px;
|
||||
font-size: 1.2em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#sidebar-separator {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 316px;
|
||||
bottom:10px;
|
||||
width: 15px;
|
||||
background: url(images/grip.png) no-repeat 50% 50%;
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.sidebar-closed > #sidebar { display: none; }
|
||||
.sidebar-closed > #sidebar-separator { right: 0px !important; }
|
||||
.sidebar-closed > #workspace { right: 15px !important; }
|
||||
.sidebar-closed > #chart-zoom-controls { right: 35px !important; }
|
||||
|
70
editor/sass/style.scss
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
@import "mixins";
|
||||
|
||||
@import "jquery";
|
||||
@import "bootstrap";
|
||||
|
||||
@import "dropdownMenu";
|
||||
|
||||
@import "header";
|
||||
@import "palette";
|
||||
@import "sidebar";
|
||||
@import "workspace";
|
||||
@import "workspaceToolbar";
|
||||
|
||||
@import "notifications";
|
||||
|
||||
@import "editor";
|
||||
@import "library";
|
||||
|
||||
@import "tabs";
|
||||
@import "tab-config";
|
||||
@import "tab-info";
|
||||
|
||||
@import "flow";
|
||||
|
||||
@import "dragdrop";
|
||||
|
||||
@import "keyboard";
|
||||
|
||||
|
||||
body {
|
||||
font: 13px "Helvetica" !important;
|
||||
padding-top: 100px;
|
||||
background: url("images/pw_maze_white.png");
|
||||
}
|
||||
|
||||
#main-container {
|
||||
position: absolute;
|
||||
top:50px; left:0; bottom: 0; right:0;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
i.spinner {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
vertical-align: text-top;
|
||||
margin-top: 0px;
|
||||
background: url(images/spin.svg) no-repeat 50% 50%;
|
||||
background-size: contain
|
||||
}
|
||||
|
||||
|
||||
|
63
editor/sass/tab-config.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
ul.tab-config-list {
|
||||
list-style-type: none;
|
||||
padding: 3px;
|
||||
margin: 0;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
ul.tab-config-list li {
|
||||
max-width: 400px;
|
||||
font-size: 13px;
|
||||
background: #f3f3f3;
|
||||
margin: 10px auto;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
div.tab-config-list-type {
|
||||
}
|
||||
|
||||
div.tab-config-list-entry {
|
||||
position: relative;
|
||||
margin: 4px 0;
|
||||
padding: 8px 4px 8px 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.tab-config-list-entry:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
div.tab-config-list-label {
|
||||
}
|
||||
div.tab-config-list-users {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 3px;
|
||||
bottom: 3px;
|
||||
line-height: 27px;
|
||||
font-size: 11px;
|
||||
background: #f6f6f6;
|
||||
float: right;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
padding: 1px 5px;
|
||||
}
|
77
editor/sass/tab-info.scss
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
table.node-info {
|
||||
font-size: 14px;
|
||||
margin: 5px;
|
||||
width: 97%;
|
||||
}
|
||||
table.node-info tr {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
table.node-info tr.blank {
|
||||
border: none;
|
||||
}
|
||||
table.node-info tr.blank td {
|
||||
padding-top: 8px;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
padding-left: 0px;
|
||||
}
|
||||
table.node-info td:first-child{
|
||||
color: #000;
|
||||
vertical-align: top;
|
||||
width: 90px;
|
||||
padding: 3px;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
table.node-info td:last-child{
|
||||
padding-left: 5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
div.node-info {
|
||||
margin: 5px;
|
||||
}
|
||||
.node-info-property-header {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.node-info-property-header:hover,
|
||||
.node-info-property-header:focus {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
.node-help {
|
||||
font-size: 16px;
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 1.953em;
|
||||
margin: 8px auto;
|
||||
}
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
font-size: 1.563em;
|
||||
margin: 8px auto;
|
||||
}
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: normal;
|
||||
font-size: 1.25em;
|
||||
margin: 8px auto;
|
||||
}
|
||||
}
|
101
editor/sass/tabs.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
ul.red-ui-tabs {
|
||||
list-style-type: none;
|
||||
padding:5px 2px 0px 5px;
|
||||
margin: 0;
|
||||
display: block;
|
||||
height: 24px;
|
||||
border-bottom: 1px solid #999;
|
||||
background: #e3e3e3;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
display: inline-block;
|
||||
border-left: 1px solid #999;
|
||||
border-top: 1px solid #999;
|
||||
border-right: 1px solid #999;
|
||||
border-bottom: 1px solid #999;
|
||||
background: #e3e3e3;
|
||||
margin: 0 5px 0 0;
|
||||
height: 23px;
|
||||
line-height: 17px;
|
||||
max-width: 200px;
|
||||
width: 14%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li a.red-ui-tab-label {
|
||||
display: block;
|
||||
padding: 3px 16px;
|
||||
color: #666;
|
||||
}
|
||||
ul.red-ui-tabs li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li a.red-ui-tab-close {
|
||||
background: rgba(227,227,227,0.8);
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 2px;
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
padding: 0px;
|
||||
border-radius: 5px;
|
||||
color: #666;
|
||||
}
|
||||
ul.red-ui-tabs li a.red-ui-tab-close:hover {
|
||||
background: #bbb !important;
|
||||
}
|
||||
ul.red-ui-tabs li a:hover {
|
||||
text-decoration: none;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li.active {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
ul.red-ui-tabs li.active a {
|
||||
color: #333;
|
||||
}
|
||||
ul.red-ui-tabs li.active a.red-ui-tab-close {
|
||||
background: rgba(255,255,255,0.8);
|
||||
}
|
||||
ul.red-ui-tabs li.active a.red-ui-tab-label:hover {
|
||||
background: #fff;
|
||||
}
|
||||
ul.red-ui-tabs li.red-ui-add-tab {
|
||||
width: 25px;
|
||||
border-top-right-radius: 15px;
|
||||
line-height: 22px;
|
||||
}
|
||||
ul.red-ui-tabs li.red-ui-add-tab a {
|
||||
padding: 2px 4px;
|
||||
}
|
76
editor/sass/workspace.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
|
||||
#chart {
|
||||
overflow: auto;
|
||||
background: #e3e3e3;
|
||||
position: absolute;
|
||||
bottom:0px;
|
||||
top: 30px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
}
|
||||
#chart svg:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#workspace {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
top:5px;
|
||||
left:190px;
|
||||
bottom: 10px;
|
||||
right: 330px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#chart-zoom-controls {
|
||||
position: absolute;
|
||||
bottom:30px; right: 350px;
|
||||
}
|
||||
|
||||
#chart-zoom-controls {
|
||||
padding-top: 3px;
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#workspace-tabs {
|
||||
margin-right: 28px;
|
||||
}
|
||||
#workspace-add-tab {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 29px;
|
||||
width: 28px;
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
#btn-workspace-add-tab {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
background: #e3e3e3;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
}
|
||||
#btn-workspace-add-tab:hover {
|
||||
background: #efefef;
|
||||
}
|
||||
|
54
editor/sass/workspaceToolbar.scss
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
|
||||
#workspace-toolbar {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left:0;
|
||||
right: 20px;
|
||||
padding: 7px;
|
||||
border-bottom-right-radius: 5px;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
#workspace-toolbar .button {
|
||||
line-height: 18px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
color: #666;
|
||||
background: #f6f6f6;
|
||||
vertical-align: middle;
|
||||
box-shadow: 0 0 2px #888;
|
||||
margin-right: 5px;
|
||||
}
|
||||
#workspace-toolbar .button.disabled {
|
||||
box-shadow: 0 0 2px #bbb;
|
||||
color: #aaa;
|
||||
cursor: default;
|
||||
}
|
||||
#workspace-toolbar .button:not(.disabled):hover {
|
||||
background: #e6e6e6;
|
||||
box-shadow: 0 0 2px #666;
|
||||
}
|
||||
#workspace-toolbar .button:not(.disabled):active {
|
||||
background: #e0e0e0;
|
||||
box-shadow: 0 0 2px #444;
|
||||
}
|