Compare commits
1229 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ca5cbb640a | ||
|
0a96259ddf | ||
|
d99b9c04e4 | ||
|
64d261e053 | ||
|
eb027d98aa | ||
|
a95727b654 | ||
|
4e636d7eec | ||
|
3cd53f617a | ||
|
b1684e82d8 | ||
|
a55027b838 | ||
|
28678acf74 | ||
|
ce6594c8cc | ||
|
75855d5450 | ||
|
f248699a30 | ||
|
29594726ca | ||
|
0a41b07297 | ||
|
e45cb7fac1 | ||
|
489dbfc72a | ||
|
a89ae7d77a | ||
|
a1eeff4034 | ||
|
0e1013a570 | ||
|
4562b06a60 | ||
|
3c96218338 | ||
|
f0a4ea099c | ||
|
c8d6693fba | ||
|
81bbdfe413 | ||
|
0e362943bf | ||
|
1e37fed90b | ||
|
aafcfef387 | ||
|
4b83d8160f | ||
|
73a41707e5 | ||
|
c989c533e8 | ||
|
707dc8c65c | ||
|
4c6157a06e | ||
|
f973396821 | ||
|
e73216d4c1 | ||
|
e6de26736b | ||
|
d131addd63 | ||
|
0c7705beff | ||
|
08b11addec | ||
|
555f96cfaf | ||
|
59ffacb3df | ||
|
83acb66f00 | ||
|
1f9ae45875 | ||
|
ffa628be2d | ||
|
8916f6f829 | ||
|
215c8fd261 | ||
|
061cc908a7 | ||
|
18a519f9ed | ||
|
7970c9dbe5 | ||
|
5ca0c066e2 | ||
|
563728c7b8 | ||
|
31a72b6562 | ||
|
d3dfbc3034 | ||
|
f143a6ba08 | ||
|
28a65923b6 | ||
|
4ca3df77b3 | ||
|
4cbe264869 | ||
|
b6b65b6bf7 | ||
|
e7cc42a927 | ||
|
bba3ca8cc0 | ||
|
8423e2d245 | ||
|
fc263718a1 | ||
|
c3a99cf5a4 | ||
|
f6820ec615 | ||
|
226ad3fe22 | ||
|
a9b17e930c | ||
|
932ea7ba8f | ||
|
c720d78c39 | ||
|
8d21e441a0 | ||
|
16ecb1a9cb | ||
|
f68acca427 | ||
|
671d7e2beb | ||
|
52fc497412 | ||
|
2084ad318f | ||
|
b530c1a43d | ||
|
f2797a4153 | ||
|
659c326f89 | ||
|
534b07d120 | ||
|
de64fc8b8d | ||
|
1e234fcb73 | ||
|
fa9a7e725b | ||
|
95b2675f03 | ||
|
564902b886 | ||
|
071a04595a | ||
|
eaa4b76ede | ||
|
74a1713e99 | ||
|
5f5aa0b2f7 | ||
|
eef59fd40e | ||
|
361ff315e9 | ||
|
eeea8e530e | ||
|
8d5286703f | ||
|
74f2180fa4 | ||
|
d042169f2e | ||
|
1fd87bf664 | ||
|
eeaff6b553 | ||
|
6efd048fd6 | ||
|
6e9e694f66 | ||
|
44a0f1b505 | ||
|
9790211891 | ||
|
be18cc9f2d | ||
|
8caee09ea4 | ||
|
26f5305593 | ||
|
d33029027f | ||
|
339aaaec57 | ||
|
db2425c473 | ||
|
18731f6055 | ||
|
34f1f7a31d | ||
|
7ef153756b | ||
|
bf90509526 | ||
|
d853eca489 | ||
|
869ae01da9 | ||
|
d63996eea1 | ||
|
9bbc8eda9d | ||
|
9cc1b03c56 | ||
|
b1ab26e3ad | ||
|
96820418b5 | ||
|
385d9f16e9 | ||
|
d56fce37dd | ||
|
aef2c9e5cf | ||
|
89a05c580f | ||
|
b85e562980 | ||
|
a0e6628757 | ||
|
60a41524f0 | ||
|
6042395b81 | ||
|
8a5db8ce4b | ||
|
196d6e79e2 | ||
|
91f16215e5 | ||
|
9c675a7847 | ||
|
f9e09e87d6 | ||
|
73574d6293 | ||
|
0a5a42b32a | ||
|
de225205bd | ||
|
8a47d36480 | ||
|
d5f3ba8d8a | ||
|
782a06ce84 | ||
|
5cdafc50fb | ||
|
0ca3cdb9ae | ||
|
a1d6cbd5fd | ||
|
6c36778cac | ||
|
1c3a97a71a | ||
|
3489fe0cf4 | ||
|
74b6d9dff9 | ||
|
06ee9aa05c | ||
|
f0f40a8606 | ||
|
1f2c9879bd | ||
|
d1eb82bdf6 | ||
|
8167f623e3 | ||
|
9555e296a2 | ||
|
f460283fa1 | ||
|
79da8e5a37 | ||
|
a35ce22218 | ||
|
1c905da8c2 | ||
|
2b558768f1 | ||
|
7607c4c882 | ||
|
c9f4813ce1 | ||
|
0428e27039 | ||
|
e30da2168d | ||
|
0cd20768f4 | ||
|
ab31f34862 | ||
|
9a4ff5cb43 | ||
|
f66c91e18e | ||
|
a235745be7 | ||
|
9516da01e3 | ||
|
7657bd2375 | ||
|
0adcea9e7c | ||
|
aa8ad60083 | ||
|
5d98a86a6b | ||
|
4418fdaed6 | ||
|
c58c45c917 | ||
|
45eba5cabd | ||
|
3ab0d0d865 | ||
|
a6803081ab | ||
|
8debed805b | ||
|
fc9835512d | ||
|
5f0cab8cc2 | ||
|
bd391963bc | ||
|
97fa28fb10 | ||
|
67d5b39c96 | ||
|
dbceef2581 | ||
|
5b22ccfca6 | ||
|
714c254bab | ||
|
90f4db9158 | ||
|
eed470ddae | ||
|
49f72881f4 | ||
|
a76674032d | ||
|
ec392a7f9a | ||
|
8a2ae6c480 | ||
|
b3796a8e24 | ||
|
b9144ff987 | ||
|
5344949c71 | ||
|
05cbba9a35 | ||
|
325c6135cf | ||
|
fdea19a45b | ||
|
fad63c0c18 | ||
|
f002560616 | ||
|
8e7d52e645 | ||
|
d119594cbf | ||
|
84f7da6e93 | ||
|
22e1bafe1b | ||
|
3f8e42e510 | ||
|
9704fb04d9 | ||
|
dcfaf1e2b9 | ||
|
42f7dc1947 | ||
|
eb1a597456 | ||
|
8368815db5 | ||
|
9c6295d0d8 | ||
|
4d19f881e9 | ||
|
3a8820397b | ||
|
85b1c1fe97 | ||
|
a4de9e94dd | ||
|
0dd2c7fe24 | ||
|
f22c3b549e | ||
|
3c60b3d2c9 | ||
|
f2d36b84b5 | ||
|
9af08ef26a | ||
|
b4be1184fd | ||
|
c60e0d389c | ||
|
d658fe7709 | ||
|
54036a2b4d | ||
|
2da9572a45 | ||
|
306825aa90 | ||
|
c797073c05 | ||
|
d9d65d59d1 | ||
|
aad29e4487 | ||
|
b00985f99f | ||
|
538a16a5fb | ||
|
300a8d3a89 | ||
|
e3b7c5fce7 | ||
|
2e87ebe800 | ||
|
7ed9e7cdd4 | ||
|
7ff9c2885d | ||
|
18c8bbb0fc | ||
|
9a49fb9450 | ||
|
84457bc7b4 | ||
|
15f1e2c85c | ||
|
15e828e975 | ||
|
e4626ee52b | ||
|
1866c9c7ef | ||
|
a0f91aa814 | ||
|
a89c7b1a70 | ||
|
ded1376957 | ||
|
252040f03b | ||
|
d29abc2724 | ||
|
44c35d2644 | ||
|
f9b972349d | ||
|
e06cadd761 | ||
|
ee45d6b48f | ||
|
d915b280d4 | ||
|
39d90fe881 | ||
|
b9da1f18b4 | ||
|
69a0934173 | ||
|
289de85754 | ||
|
0ec95041d9 | ||
|
fcb6f78d54 | ||
|
29e9740668 | ||
|
ea8c6d5cce | ||
|
e4c951984a | ||
|
0071afb205 | ||
|
d3c7ac75be | ||
|
55d7420abf | ||
|
489b56456f | ||
|
1f24fcb364 | ||
|
722b31edee | ||
|
765f0393b0 | ||
|
6868ef044b | ||
|
5dd0622e40 | ||
|
48bdab1dcf | ||
|
aee483e9f1 | ||
|
8542b9bf67 | ||
|
feaf6f2501 | ||
|
d7d30aa972 | ||
|
91c23d1f7d | ||
|
57479edc59 | ||
|
4b462eaae9 | ||
|
c60fb3bc25 | ||
|
b17c34402d | ||
|
6ad71bd222 | ||
|
ccc08be0ee | ||
|
2a2fc80931 | ||
|
9ebca91775 | ||
|
456fc23463 | ||
|
eb17562f4d | ||
|
b7dbfd5cfc | ||
|
cdc7ab562a | ||
|
e6b5552cba | ||
|
eecf92183f | ||
|
11656382a7 | ||
|
e4d788ad0b | ||
|
3017442702 | ||
|
ba37db275c | ||
|
521e669879 | ||
|
12e302c10a | ||
|
7220af3ef0 | ||
|
42f4e0fa86 | ||
|
022d066fe0 | ||
|
4c9d7cbeed | ||
|
1541e382e4 | ||
|
1d9488d24f | ||
|
6cbc1afb9b | ||
|
3f86b660ed | ||
|
4603f2d9ca | ||
|
da818cf420 | ||
|
86a2ed652d | ||
|
269761f222 | ||
|
53ca3046b3 | ||
|
f484156d8e | ||
|
1da8712a4a | ||
|
dd47eba88c | ||
|
0ade8ff7a2 | ||
|
6a528b5fdb | ||
|
7f63ddc9ea | ||
|
d6b326c134 | ||
|
d944298dd7 | ||
|
0136ebd2b4 | ||
|
45f8def1ed | ||
|
7c6e8eeefc | ||
|
e81e48cde3 | ||
|
8eebb6ea2d | ||
|
ad8290ebcb | ||
|
15b6f6268b | ||
|
92d5af7446 | ||
|
d57425a15e | ||
|
a457c06500 | ||
|
906bbae899 | ||
|
e360e57a5b | ||
|
c5753a013c | ||
|
691b083364 | ||
|
b74a35b9d1 | ||
|
82269462a4 | ||
|
d7943aab28 | ||
|
446eb8e978 | ||
|
d91a99c833 | ||
|
dc00870461 | ||
|
b8f578862e | ||
|
dc24c05229 | ||
|
f62cf6818b | ||
|
c05e9da9c5 | ||
|
6c00194d35 | ||
|
6bc3f82afe | ||
|
12e46deea2 | ||
|
8608d010b8 | ||
|
9d4d1acf2d | ||
|
23087447f1 | ||
|
3008e4e60f | ||
|
9d52ed5ff6 | ||
|
5f047633c3 | ||
|
d3be1f1e2c | ||
|
c3b1cf7c35 | ||
|
682345da22 | ||
|
82f289c42e | ||
|
78eae99bd4 | ||
|
9ec7bb8d41 | ||
|
2b9bfbc309 | ||
|
e50d04077b | ||
|
b6fcaacb77 | ||
|
939da4e551 | ||
|
f30ce1f9eb | ||
|
343588b2a0 | ||
|
77b13ce9d4 | ||
|
7cb41d2ca9 | ||
|
8bbc9e6502 | ||
|
e29a0df3fd | ||
|
0eba04aac0 | ||
|
b78210e3be | ||
|
958de21be8 | ||
|
0eb4742982 | ||
|
78b1bf8f25 | ||
|
08f2741871 | ||
|
98b24ae630 | ||
|
7fc056c8e3 | ||
|
0411623857 | ||
|
365d71264f | ||
|
0d4d51fc39 | ||
|
b21745808b | ||
|
e4d5271d58 | ||
|
8f2f3bf75d | ||
|
9e96eba98f | ||
|
0441c83fd7 | ||
|
d8405052d8 | ||
|
cc02b07ff0 | ||
|
2d6aac7d6f | ||
|
589d43f0e5 | ||
|
13c1d1df7a | ||
|
08ade44dc8 | ||
|
8fb1c76247 | ||
|
3ad9053d65 | ||
|
8fe07e0f07 | ||
|
09b069c129 | ||
|
b2db083f39 | ||
|
53e2f3e263 | ||
|
945fbbc065 | ||
|
4dc9c7714c | ||
|
7302ac5871 | ||
|
1cfad27d6f | ||
|
d82fe95076 | ||
|
8f8df4971c | ||
|
fd66569950 | ||
|
e97d9fb0b2 | ||
|
04424c2a7c | ||
|
241e2828e7 | ||
|
5f6a0141f0 | ||
|
ef2f71859c | ||
|
fdaeeb5d01 | ||
|
e594ffe0f8 | ||
|
9f8c32ce8f | ||
|
762eb07dd4 | ||
|
0300458ba8 | ||
|
3959fcdc88 | ||
|
ea76c18f59 | ||
|
d125ecc671 | ||
|
7d9b90a1f3 | ||
|
7402c27b6a | ||
|
4e762e2063 | ||
|
0afe98b399 | ||
|
daed059c47 | ||
|
f1ce0fab8b | ||
|
b5d3f505e3 | ||
|
6c8f688f33 | ||
|
ed1b601a84 | ||
|
add541f67f | ||
|
bea8eb799f | ||
|
3cac48e86f | ||
|
64722da4a7 | ||
|
ada1e624d8 | ||
|
69f83cb905 | ||
|
807873f685 | ||
|
8d4be848b0 | ||
|
59a7c46482 | ||
|
eabfeb9502 | ||
|
291240dd94 | ||
|
2f6ed47168 | ||
|
9a73568c7a | ||
|
acdef87be7 | ||
|
b14546605d | ||
|
5ad46106f4 | ||
|
3e9be9eed3 | ||
|
7318a7b767 | ||
|
b78682f413 | ||
|
e50659af09 | ||
|
3454e5ac77 | ||
|
9e26aeea1d | ||
|
d7715b05ee | ||
|
db433efbef | ||
|
f1f8c887c6 | ||
|
9ae4745ca5 | ||
|
726d9c8ec5 | ||
|
a9bfa4e79b | ||
|
8e6bba143a | ||
|
feeba77f16 | ||
|
ea41a0e842 | ||
|
74b7500181 | ||
|
594ff8cd3d | ||
|
337f5f9b98 | ||
|
41445a1b48 | ||
|
269763fa0c | ||
|
fa90eeac55 | ||
|
edceffdaaf | ||
|
ce25fc658b | ||
|
b27db3e2e7 | ||
|
622d4214f7 | ||
|
0c53b5310a | ||
|
45ff86eae5 | ||
|
47316b0fb7 | ||
|
c09be02e4e | ||
|
bd59398cab | ||
|
8080ebceb4 | ||
|
1e2521c37a | ||
|
b744491dd2 | ||
|
2a089f7d90 | ||
|
088e3e5374 | ||
|
bac8a3092f | ||
|
e56da17957 | ||
|
71b2e714ee | ||
|
1b06afb81c | ||
|
819e48b03a | ||
|
1861c1feb6 | ||
|
0efccc4758 | ||
|
a9feeaa1c9 | ||
|
f9c869f521 | ||
|
9c766d76f3 | ||
|
333acccff6 | ||
|
1790ebf567 | ||
|
6354b68bae | ||
|
41b10fd5e4 | ||
|
4ad540412a | ||
|
8916cf273e | ||
|
b2923d0fc4 | ||
|
8fc0018cb9 | ||
|
d0f57efe0b | ||
|
595ff63b72 | ||
|
9990046abb | ||
|
8dacf72b3c | ||
|
2801838ffa | ||
|
59b34c2b3f | ||
|
f55f85aa14 | ||
|
627a80419a | ||
|
4bc482bc85 | ||
|
0a1257a23a | ||
|
51d99248d7 | ||
|
af9aa74337 | ||
|
321f5e615b | ||
|
95c31f3e17 | ||
|
cf69dbe1dc | ||
|
e92241bf97 | ||
|
44dc37ef6d | ||
|
6b0bef61a5 | ||
|
0c227be02d | ||
|
08794bad74 | ||
|
44693dd23a | ||
|
75a7be41eb | ||
|
913b09570c | ||
|
1c9b5dfd00 | ||
|
2954ae917b | ||
|
736ddaeca4 | ||
|
b909e32201 | ||
|
bcff74327b | ||
|
e60b63e72f | ||
|
2bf5d2b4e5 | ||
|
e1d09349ff | ||
|
f07c8108fc | ||
|
39f5078d6b | ||
|
1d54761d48 | ||
|
7cb9b2da66 | ||
|
b1896e3737 | ||
|
6fa78bdb04 | ||
|
ee96f7d937 | ||
|
129ca0e39f | ||
|
906703db5f | ||
|
0cd4a2b4ec | ||
|
aef8aaa0bd | ||
|
428fbb8622 | ||
|
b9f03e7d80 | ||
|
db686388b9 | ||
|
626cba4002 | ||
|
37d4a6b9e2 | ||
|
12c4561aba | ||
|
fed49e3718 | ||
|
3af37d3984 | ||
|
0f49a11228 | ||
|
27d3e165b0 | ||
|
e941c22f6c | ||
|
7281e4deb6 | ||
|
f2191e94b3 | ||
|
349ebfe4db | ||
|
708365c4ac | ||
|
0e9ea0aff1 | ||
|
63ba05a193 | ||
|
4b702815cf | ||
|
55e66ebcac | ||
|
dcd8b3699c | ||
|
0e2d13172a | ||
|
2e2556fdad | ||
|
859a7538e1 | ||
|
0d1543ee8a | ||
|
d3a98dd355 | ||
|
ad10125303 | ||
|
b89e866d39 | ||
|
afbaf1cfe0 | ||
|
b3be8b30e7 | ||
|
8bfab8f73d | ||
|
c6ad2c9ad2 | ||
|
3b44d9972e | ||
|
af736c98f2 | ||
|
f1377fa217 | ||
|
2ba146b9ff | ||
|
2361607aa3 | ||
|
86ffc80098 | ||
|
7f6915eb59 | ||
|
d69bcad028 | ||
|
4cb45e2712 | ||
|
b7a0ad703a | ||
|
7610b9a975 | ||
|
7d95f621df | ||
|
bba210e112 | ||
|
3a97e20bde | ||
|
4fe7ea00b0 | ||
|
3ec8ecd4de | ||
|
401e65e852 | ||
|
e7c5b691a0 | ||
|
9f3ea8da67 | ||
|
d1269b441d | ||
|
7615743aa5 | ||
|
dcaa0eeea4 | ||
|
1d5e2f703e | ||
|
4d84d624b1 | ||
|
633a6a0ee6 | ||
|
c7bcd3f438 | ||
|
d3a29a6f16 | ||
|
827711ca89 | ||
|
76e98f74fa | ||
|
fb09f4b22d | ||
|
bb06585748 | ||
|
c76ba1dcc7 | ||
|
a115301b04 | ||
|
72917117a9 | ||
|
6567739236 | ||
|
4aa6b47c0e | ||
|
03558b012c | ||
|
3288efdad6 | ||
|
3902a343f3 | ||
|
882b7d0391 | ||
|
81f082825d | ||
|
392fd6fed3 | ||
|
51afed4041 | ||
|
17e3b71d9c | ||
|
6e75089f3a | ||
|
6dc640b129 | ||
|
27cbaac343 | ||
|
fa4006619e | ||
|
cb8fe8462a | ||
|
abd51a5511 | ||
|
a0cc1e6b0c | ||
|
50399c6bfa | ||
|
de48c1be44 | ||
|
0786ec4b66 | ||
|
db319e0ebc | ||
|
4fc568856a | ||
|
4c6771669b | ||
|
9bca2a91c9 | ||
|
66eaaf5a48 | ||
|
9837f0e2e1 | ||
|
6b8ffb4c68 | ||
|
f35dd34da9 | ||
|
ed19e4fa08 | ||
|
661e1a4f90 | ||
|
5826de76ca | ||
|
05888740e5 | ||
|
41f3b0c333 | ||
|
70f3e72a20 | ||
|
e873afd40b | ||
|
2777c2937a | ||
|
798903e4cc | ||
|
58622ba18f | ||
|
c368dcd5b7 | ||
|
0b4c652ce7 | ||
|
dbaacc411a | ||
|
1850185d1e | ||
|
2e9d445d36 | ||
|
aed89d82fb | ||
|
231adac6d8 | ||
|
587c4e5915 | ||
|
55f1cbf18f | ||
|
38168a545b | ||
|
43c6df49d7 | ||
|
f1c59faf72 | ||
|
5f7019325c | ||
|
fe4dae8518 | ||
|
1f848b205b | ||
|
742c470d81 | ||
|
5ead3342cc | ||
|
b95dc2ecce | ||
|
4d0950215f | ||
|
da0ce9fe0d | ||
|
ca62e720b5 | ||
|
c4b1795396 | ||
|
fd2e47ed73 | ||
|
d5f2255a68 | ||
|
05b58e9263 | ||
|
4a91c27e4b | ||
|
3a03d46d8d | ||
|
f03aff7006 | ||
|
043b8a3105 | ||
|
1dd9984521 | ||
|
d2be7f8c8f | ||
|
88dc202db2 | ||
|
083d54b008 | ||
|
87d77efa57 | ||
|
35c4a41d7b | ||
|
1ca3ca07d5 | ||
|
d673846e3d | ||
|
f62b7afede | ||
|
e65770a53a | ||
|
a92a741932 | ||
|
45f67191ba | ||
|
93f5da325b | ||
|
8fb955e182 | ||
|
9f5e6a4b37 | ||
|
f43738446e | ||
|
923a46d304 | ||
|
b9b5eaccae | ||
|
cda11491c2 | ||
|
98c539f662 | ||
|
9fb958b302 | ||
|
8e25e76439 | ||
|
62694da7e6 | ||
|
86064651af | ||
|
65daaeb617 | ||
|
08b39f50b3 | ||
|
d0f7e5ca4d | ||
|
4eb5058e68 | ||
|
1054193298 | ||
|
38c6cf0450 | ||
|
5b04b86867 | ||
|
a074bcfd56 | ||
|
f93179d946 | ||
|
2c347bc092 | ||
|
0f7119f468 | ||
|
2685a24705 | ||
|
371f72f4f1 | ||
|
c70c00043b | ||
|
50d0a88276 | ||
|
5bbf576dae | ||
|
5d334e9619 | ||
|
98f9353338 | ||
|
d3de7037e5 | ||
|
64431c6711 | ||
|
d4ce193dc8 | ||
|
606305aec4 | ||
|
a95f44d68b | ||
|
ef2dc4b9e1 | ||
|
9baca1772b | ||
|
04cd19349d | ||
|
1280e5bc8b | ||
|
dda90f956d | ||
|
bc4b599513 | ||
|
090d52d678 | ||
|
a47ad4842a | ||
|
3d5ed840dc | ||
|
11d75ff581 | ||
|
306fb7a3d1 | ||
|
0839b6f58e | ||
|
fceca703b3 | ||
|
4dc60d2477 | ||
|
d840d0b67d | ||
|
43dad4c465 | ||
|
60812b2d8a | ||
|
35e2caff13 | ||
|
1d9d5c6bc7 | ||
|
4d99536ea7 | ||
|
34537180c3 | ||
|
cb01920ee6 | ||
|
437b01a0ff | ||
|
075a2abf71 | ||
|
985875cc75 | ||
|
1c45bc615f | ||
|
fa7f2606fb | ||
|
12b95f1c72 | ||
|
a0aee2021d | ||
|
c90fd1e6d8 | ||
|
3b769fd2de | ||
|
71ecb89abc | ||
|
7b6bc1d3bc | ||
|
9c3be40fbe | ||
|
ab87fa9ce4 | ||
|
d1940a023a | ||
|
5a176a037c | ||
|
ec25191c98 | ||
|
20b321f928 | ||
|
425b016d63 | ||
|
b2c7189ce4 | ||
|
f66886dbdb | ||
|
712f8b4680 | ||
|
f626ee060a | ||
|
86aa7c97be | ||
|
30e3525987 | ||
|
ad44f838da | ||
|
2569a35b6c | ||
|
1ee5e50d50 | ||
|
1dbec5eca8 | ||
|
2bc8db308c | ||
|
f196740426 | ||
|
20121b79c5 | ||
|
741a4cfe53 | ||
|
0343de9f34 | ||
|
4772bca14a | ||
|
6ae1a5ba0d | ||
|
217c9718e4 | ||
|
61d7893467 | ||
|
8f26c01f4b | ||
|
61045ddd7f | ||
|
1bf72a0bc3 | ||
|
6d84b1bb8d | ||
|
8abd0b1fdf | ||
|
81e125b7ba | ||
|
d5e1468718 | ||
|
c232bf5ed6 | ||
|
21b25ffaee | ||
|
ca0a93df08 | ||
|
699a22c757 | ||
|
8b2b1669b5 | ||
|
c1e8370916 | ||
|
ddedea8b90 | ||
|
8f414ce458 | ||
|
453f23da20 | ||
|
9e91e42a1b | ||
|
b666734c79 | ||
|
a2297f303d | ||
|
ecde942255 | ||
|
d668d43a0a | ||
|
ca91a5dd95 | ||
|
5f9780d71c | ||
|
ee37464741 | ||
|
8d73f927db | ||
|
4a0222bd1c | ||
|
c0b8f5e3e1 | ||
|
a9a0b263dc | ||
|
f2b73187d8 | ||
|
ef10ade0cc | ||
|
719bb4263e | ||
|
b3602b268e | ||
|
66ec9bae27 | ||
|
d96b6e77c0 | ||
|
da64c018ac | ||
|
f9fb97adf2 | ||
|
8316bc6480 | ||
|
08021e039a | ||
|
cc6e0937a0 | ||
|
c1d694a97c | ||
|
fcf4f40c36 | ||
|
380b03399c | ||
|
c33d02c53f | ||
|
fa5e37993e | ||
|
437b2d506b | ||
|
4ed09f6431 | ||
|
0b98a6acf8 | ||
|
1d73c86cb2 | ||
|
40fe0f3239 | ||
|
d1ea689999 | ||
|
a6644ad5ff | ||
|
658746d2a3 | ||
|
cbdd4de630 | ||
|
00c612485b | ||
|
3a6192bf73 | ||
|
c64b5c2850 | ||
|
fdbf079896 | ||
|
d1a5395727 | ||
|
83a3642c0e | ||
|
9932d34304 | ||
|
7aa37a1976 | ||
|
fa42fbdab8 | ||
|
caa83ac830 | ||
|
3963fa9738 | ||
|
005a98d020 | ||
|
9560dc9408 | ||
|
4ac9a5edf0 | ||
|
37e62597ae | ||
|
90bfe378d0 | ||
|
ce22b494ec | ||
|
f9e0420647 | ||
|
2fe568d9ba | ||
|
2d4979df4d | ||
|
b555b014b8 | ||
|
999b888c54 | ||
|
5193d7bddb | ||
|
6b03379e4e | ||
|
08d687ad60 | ||
|
eb57089f06 | ||
|
a76e4fede1 | ||
|
705d043540 | ||
|
5462e251f8 | ||
|
50788af6ca | ||
|
1a07c5a329 | ||
|
9fb81b2814 | ||
|
10ad7fbf6e | ||
|
811c4f2630 | ||
|
f7e3b0a64f | ||
|
7d83d76fb3 | ||
|
d3c41b38f7 | ||
|
57d6b16d5c | ||
|
27aa5ae7db | ||
|
62e8f564b9 | ||
|
a1d7bb4208 | ||
|
5d8dae05c4 | ||
|
6bde07b5a0 | ||
|
846ab08661 | ||
|
999cf66b27 | ||
|
60539d890b | ||
|
e5a0f25d94 | ||
|
fde9d40098 | ||
|
f70e9ea076 | ||
|
d0af4aac4d | ||
|
7de3704210 | ||
|
1c33b837b8 | ||
|
95d20d7fba | ||
|
90b8806e7c | ||
|
39df80bf99 | ||
|
bac4beae03 | ||
|
a7b68c18b5 | ||
|
ef2360baee | ||
|
8716e7e601 | ||
|
00c2dae969 | ||
|
8782bc5896 | ||
|
6359b90352 | ||
|
6cfa4976fe | ||
|
35cd7cf2b8 | ||
|
b2d7f079b7 | ||
|
726069bc4b | ||
|
fbccf01933 | ||
|
c9f3c6f4a3 | ||
|
c47da013ff | ||
|
bc76499957 | ||
|
24afcff0ea | ||
|
6777f24845 | ||
|
61e0923fc4 | ||
|
757caeb9a4 | ||
|
fe9dc6b272 | ||
|
24efdbe4da | ||
|
9ca102cf81 | ||
|
48df31d7b7 | ||
|
539afb1e1d | ||
|
bdcba44ca5 | ||
|
99a51b07ac | ||
|
5fbaca75b4 | ||
|
a6974371b0 | ||
|
409fa49234 | ||
|
9bbd6a70b8 | ||
|
f0b4cb608a | ||
|
284d7e26d1 | ||
|
965c0937ac | ||
|
d9cf6a4431 | ||
|
e6ed8ee509 | ||
|
2563649b3e | ||
|
203bc41b06 | ||
|
cb1d18c7c8 | ||
|
5ea68dafc4 | ||
|
68bb8252af | ||
|
406f742d29 | ||
|
5522e57f65 | ||
|
94e27dbfc5 | ||
|
f5fc8f763f | ||
|
9058bf615c | ||
|
9d17137dec | ||
|
4a318553f7 | ||
|
0017074d38 | ||
|
a39a26fcc2 | ||
|
2fe859b111 | ||
|
c105b2df37 | ||
|
4fb86ab55a | ||
|
1ed98a5963 | ||
|
a4a29ceb3c | ||
|
6249083431 | ||
|
aa18c65fa8 | ||
|
a7900940da | ||
|
008bc98070 | ||
|
0705589cc2 | ||
|
b2caba593f | ||
|
6d4c64fcd5 | ||
|
7d41781fb4 | ||
|
0760facb77 | ||
|
a0ce095807 | ||
|
df0110913a | ||
|
06731374a4 | ||
|
6c8b7c0082 | ||
|
93136961b9 | ||
|
529a691e1d | ||
|
9f0b3eba47 | ||
|
6d897793cb | ||
|
0e12fc6b02 | ||
|
c00558ea1b | ||
|
bacf27a3ca | ||
|
6560ea0630 | ||
|
8338231ce5 | ||
|
5813a91244 | ||
|
dfd8ab3545 | ||
|
a5b9b949a8 | ||
|
72570e4510 | ||
|
3002aead6b | ||
|
841edbe6fb | ||
|
2b9aca0c56 | ||
|
3a17c3ee6d | ||
|
df09252ee0 | ||
|
f51778d417 | ||
|
119fc63794 | ||
|
ee3425d3be | ||
|
31c979f30f | ||
|
89f2c26cd6 | ||
|
0dbf43d0aa | ||
|
8a6d11b191 | ||
|
d8eb926e2c | ||
|
21d0adbdae | ||
|
c5fd3a5753 | ||
|
eae4e3d983 | ||
|
bb1fe8daef | ||
|
c5d8e09b41 | ||
|
369eae3d92 | ||
|
e2fa457ca2 | ||
|
871f764e98 | ||
|
f8853af902 | ||
|
4248d20f39 | ||
|
5cda08e7b0 | ||
|
7e9d96ee87 | ||
|
974ac31d33 | ||
|
0658b70631 | ||
|
53258eeede | ||
|
4f174308b9 | ||
|
98c9e40349 | ||
|
3a4756bd83 | ||
|
6ccb05cb2c | ||
|
3c2d32b867 | ||
|
956050434f | ||
|
38ab1550d2 | ||
|
e852d1e57c | ||
|
7de0216976 | ||
|
911288e695 | ||
|
72e1f20383 | ||
|
d28a6eaf9d | ||
|
17f3366556 | ||
|
0bef04ae0a | ||
|
f11b906fd9 | ||
|
518358d9dc | ||
|
5ffde21d83 | ||
|
052302b3e7 | ||
|
fe1ce21114 | ||
|
ce5c9da107 | ||
|
cf25b2866e | ||
|
07fd5a5f5f | ||
|
913fdac671 | ||
|
7dc838dea6 | ||
|
7112fd2a22 | ||
|
56e8c143dd | ||
|
7b4cbbe816 | ||
|
8f8ee4662d | ||
|
86013c7db4 | ||
|
01aa3324f8 | ||
|
b9cfeee965 | ||
|
b3684a70b5 | ||
|
51fce9343b | ||
|
0c5c3448d0 | ||
|
deaef3ab86 | ||
|
a443491c0c | ||
|
276d893198 | ||
|
653d0e71e4 | ||
|
faa7d948a7 | ||
|
771342989e | ||
|
e9ce519e4b | ||
|
c016b102eb | ||
|
0583c60837 | ||
|
1c1a85dcef | ||
|
c71e76335b | ||
|
c1a32c4eb9 | ||
|
9ad1f769d3 | ||
|
87f8fd34b8 | ||
|
e206d2919e | ||
|
9d809aa2ba | ||
|
8f744794e4 | ||
|
78ab4217be | ||
|
d090df94c5 | ||
|
937f26da41 | ||
|
98e3ff014e | ||
|
6f84526364 | ||
|
105d38c885 | ||
|
d7bdcd69fc | ||
|
87e537da90 | ||
|
8f16695f06 | ||
|
8403f6291f | ||
|
5af6ac3e80 | ||
|
0d557094b2 | ||
|
a2aa78afd4 | ||
|
b0de8abb63 | ||
|
6ff540ed08 | ||
|
2b8ed9850b | ||
|
dcd579b5e3 | ||
|
c9d2d301aa | ||
|
1aaef598a5 | ||
|
73d1f3d0e8 | ||
|
e369ded6c5 | ||
|
269846c587 | ||
|
8dc98420db | ||
|
1014abe92f | ||
|
6927f10f8f | ||
|
49d3a7190a | ||
|
a2e65b0018 | ||
|
f48ee01a03 | ||
|
0e926c566b | ||
|
d2c4c2c34c | ||
|
c2253d1e25 | ||
|
eae16b6e8c | ||
|
868ae5b5dd | ||
|
1406503e10 | ||
|
9ca9d88546 | ||
|
203d3f672c | ||
|
698b2688f6 | ||
|
be1620dd07 | ||
|
e1f0969957 | ||
|
e1dd8cf2ab | ||
|
8ee90777ee | ||
|
2fe9c1e55f | ||
|
9dd7e2e43d | ||
|
5efbdf5d04 | ||
|
5be3472413 | ||
|
a9a0953653 | ||
|
d4ac4c44d0 | ||
|
f459ff8ad0 | ||
|
b96ea36b70 | ||
|
e543cc0fed | ||
|
0b1b4df210 | ||
|
e59ffb0b19 | ||
|
120c8f2c28 | ||
|
fbfc5c8a2d | ||
|
31b018c80e | ||
|
255d708fb6 | ||
|
78d1da5fbc | ||
|
9c22a770ef | ||
|
b201828236 | ||
|
2a8a885271 | ||
|
7adefd6ee0 | ||
|
f967a5ecdc | ||
|
c8d6dc2531 | ||
|
216b5fba7a | ||
|
f4ec9a72d5 | ||
|
cf0c2825eb | ||
|
be46c419dc | ||
|
62c68d06fe | ||
|
4f4d8419bc | ||
|
16e17954b4 | ||
|
cc1d080a5a | ||
|
dd7f4f6752 | ||
|
9daeba02b5 | ||
|
8a96dbd121 | ||
|
2a57d0b6d0 | ||
|
8a5c1bade5 | ||
|
fcc6943f98 | ||
|
72a9de058d | ||
|
8748be28b7 | ||
|
20bdea7ae0 | ||
|
e19b8d35a9 | ||
|
81df74dfc8 | ||
|
153fa7478f | ||
|
500e9a4010 | ||
|
5352fc87ee | ||
|
f07fd64ffb | ||
|
36f299c031 | ||
|
78cf310c58 | ||
|
18a3d71024 | ||
|
26db1048f9 | ||
|
b61a250d58 | ||
|
1d10eba0cc | ||
|
35042132fa | ||
|
57c049b49f | ||
|
7a0ce0c957 | ||
|
eb4cadb0b5 | ||
|
ac0ca083c0 | ||
|
9afb4a9315 | ||
|
df065e94b7 | ||
|
a9789697e7 | ||
|
2d91be8814 | ||
|
5c58b0c2f4 | ||
|
f0139f9808 | ||
|
5610a3184e | ||
|
b202c73708 | ||
|
dd4cec84bf | ||
|
a1dac1e290 | ||
|
e199d6725e | ||
|
aef38b945d | ||
|
cd5eac2cbb | ||
|
8fea443e71 | ||
|
2a47951e46 | ||
|
5234fda266 | ||
|
e63067cd9f | ||
|
be61cf6a88 | ||
|
5efc89d514 | ||
|
9c104faff3 | ||
|
cf8fe16b09 | ||
|
71db193675 | ||
|
9952d9451e | ||
|
fb738ad9fa | ||
|
46f2f752b0 | ||
|
42730b8fce | ||
|
1c2be579d9 | ||
|
51e891ff88 | ||
|
731efe1c01 | ||
|
f77dd06e65 | ||
|
af20f3df64 | ||
|
4078212089 | ||
|
7bdb3181e2 | ||
|
933608aec1 | ||
|
1d7f06bbba | ||
|
f26cadab7f | ||
|
eacf41a4f6 | ||
|
ab3e64271b | ||
|
e26ea14104 | ||
|
3967e23828 | ||
|
1f8c6f87c9 | ||
|
f6203fe60a | ||
|
06bf710515 | ||
|
0f3cc3196c | ||
|
4403a00651 | ||
|
9c46feb22b | ||
|
10277aa956 | ||
|
ff093d98c6 | ||
|
acc0e0875b | ||
|
69f85bd688 | ||
|
910d983b82 | ||
|
128415bc9e | ||
|
082ce798d8 | ||
|
234abd82a2 | ||
|
3cbc1bbb1b | ||
|
0ed9f6cc4f | ||
|
10b092a9a7 | ||
|
444a897410 | ||
|
e013afb053 | ||
|
34364f5627 | ||
|
cef378d820 | ||
|
a27353c166 | ||
|
bbd197c71d | ||
|
fabf013714 | ||
|
81dcfecb4e | ||
|
971a62ebc9 | ||
|
04f2c92ba6 | ||
|
00d0f8cfc7 | ||
|
c5c404ea05 | ||
|
c80a44933c | ||
|
5599b999ec | ||
|
172cbdaa84 | ||
|
a3c4f12764 | ||
|
bf1cd457cd | ||
|
8af50a51ba | ||
|
ddf31e87b2 | ||
|
5adbc012f3 | ||
|
393fc349b9 | ||
|
dfed4963ed | ||
|
131adb6f4e | ||
|
a8b3cbb683 | ||
|
e97d5c7354 | ||
|
061c44f958 | ||
|
f5d8433341 | ||
|
f78a71e8ed | ||
|
4d48c72146 | ||
|
71ff828947 | ||
|
b6245bdef7 | ||
|
ce1cd1ab9c | ||
|
54b0debb3b | ||
|
1876b56022 | ||
|
d148a23ed6 | ||
|
049a5f1be6 | ||
|
f3880b7601 | ||
|
fbb45a8961 | ||
|
b8c460b825 |
24
.gitignore
vendored
@@ -1,11 +1,19 @@
|
||||
node_modules
|
||||
credentials.json
|
||||
flows*.json
|
||||
.DS_store
|
||||
.config.json
|
||||
.dist
|
||||
.jshintignore
|
||||
.npm
|
||||
.project
|
||||
.sessions.json
|
||||
.settings
|
||||
.tern-project
|
||||
*.backup
|
||||
*_cred*
|
||||
coverage
|
||||
credentials.json
|
||||
flows*.json
|
||||
nodes/node-red-nodes/
|
||||
.npm
|
||||
/coverage
|
||||
.config.json
|
||||
.sessions.json
|
||||
|
||||
node_modules
|
||||
public
|
||||
locales/zz-ZZ
|
||||
nodes/core/locales/zz-ZZ
|
||||
|
14
.jshintrc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"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
|
||||
"proto": true // allow setting of __proto__ in node < v0.12
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
.git/*
|
||||
*.json
|
||||
lib/*
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
||||
|
7
.npmignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.settings
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.project
|
||||
.tern-project
|
||||
.travis.yml
|
||||
.git
|
20
.travis.yml
@@ -1,9 +1,21 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
before_install:
|
||||
- npm install -g npm@~1.4.18
|
||||
env:
|
||||
- CXX="g++-4.8"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- gcc-4.8
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "7"
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
- "7"
|
||||
- "6"
|
||||
- "4"
|
||||
script:
|
||||
- istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage
|
||||
before_script:
|
||||
|
619
CHANGELOG.md
Normal file
@@ -0,0 +1,619 @@
|
||||
#### 0.16.0: Milestone Release
|
||||
|
||||
Nodes
|
||||
|
||||
- Add option to colourise debug console output Closes #1103
|
||||
- Add property validation to nodes using typedInput
|
||||
- Add common validator for typedInput fields Closes #1104
|
||||
- Update debug node console logging indicator icon Closes #1094
|
||||
- Let exec node (spawn) handle commands with spaces in path
|
||||
- Add symbol to debug node to indicate debugging also to console.log
|
||||
- Change file node to use node 4 syntax (drops support for 0.8)
|
||||
- add info for httprequest responseUrl property
|
||||
- Add res.responseUrl to httprequest node response
|
||||
- Add support for flow and global context in Template node (#1048)
|
||||
- Added YAML parser node (#1034)
|
||||
- node-red-node-serialport removed as a default node
|
||||
|
||||
Editor
|
||||
|
||||
- Add install/remove dialog to increase friction Closes #1109
|
||||
- Report node catalogue load errors Closes #1009
|
||||
- Properly report module remove errors in palette editor Fixes #1043
|
||||
- Update rather than hide install button after success install
|
||||
- Tweak search box styling
|
||||
- Display info tips slightly longer
|
||||
- Allow tips to be enabled/disabled via menu option
|
||||
- Info-tips update
|
||||
- Make typedInput keyboard navigable
|
||||
- update Font Awesome to 4.7.0
|
||||
- Add expression editor for jsonata
|
||||
- Overhaul keyboard handling and introduce editor actions
|
||||
- Add Japanese translation file(editor.json) (#1084)
|
||||
- Add quick-add node mode with cmd/ctrl-click
|
||||
- Add cmd/ctrl-click to quick add wires
|
||||
- Use json-stringify-safe to detect circular references in debug msgs
|
||||
- debug - format if time if correct length/range
|
||||
- Make Debug object explorable
|
||||
- Initial debug pop-out window
|
||||
- Add proper three-way diff view
|
||||
- Focus tray body when edit dialog opened
|
||||
- Hit enter to edit first node in selection
|
||||
- Add node delete button to edit dialog
|
||||
- Add notification when runtime stopped due to missing types Part of #832
|
||||
|
||||
Fixes
|
||||
|
||||
- Do not tie debug src loading to needsPermission Fixes #1111
|
||||
- Initialise nodeApp regardless of httpAdmin setting Closes #1096 #1095
|
||||
- Speed up reveal of search dialogs
|
||||
- Ensure flows exist before delegating status/error events Fixes #1069
|
||||
- Update package dependencies
|
||||
- Update MQTT to latest 2.2.1
|
||||
- Node status not being refreshed properly in the editor
|
||||
- Try to prevent auto-fill of password fields in node edit tray Fixes #1081
|
||||
- Fix whitespace in localfilesystem
|
||||
- fix bug where savesettings did not honor local settings variables (#1073)
|
||||
- Tidy up unused/duplicate editor messages Closes #922
|
||||
- Property expressions must not be blank
|
||||
- Tidy up merge commit of validatePropertyExpression
|
||||
- add port if wires array > number of ports declared.
|
||||
- Allow quoted property expressions Fixes #1101
|
||||
- Index all node properties for node search
|
||||
- Remove node 0.10 from travis config
|
||||
- update welcome message to use logger so it can be turned off/on if required (#1083)
|
||||
- Fix dynamically loading multiple node-sets from palette editor
|
||||
- Allow a node to reorder its outputs and maintain links Fixes #1031
|
||||
|
||||
#### 0.15.3: Maintenance Release
|
||||
|
||||
- Tcpgetfix: Another small check (#1070)
|
||||
- TCPGet: Ensure done() is called only once (#1068)
|
||||
- Allow $ and _ at start of property identifiers Fixes #1063
|
||||
- TCPGet: Separated the node.connected property for each instance (#1062)
|
||||
- Corrected 'overide' typo in XML node help (#1061)
|
||||
- TCPGet: Last property check (hopefully) (#1059)
|
||||
- Add additional safety checks to avoid acting on non-existent objects (#1057)
|
||||
- add --title for process name to command line options
|
||||
- add indicator for fire once on inject node
|
||||
- reimplement $(env var) replace to share common code.
|
||||
- Fix error message for missing node html file, and add test.
|
||||
- Let credentials also use $(...) substitutions from ENV
|
||||
- Rename insecureRedirect to requireHttps
|
||||
- Add setting to cause insecure redirect (#1054)
|
||||
- Palette editor fixes (#1033)
|
||||
- Close comms on stopServer in test helper (#1020)
|
||||
- Tcpgetfix (#1050)
|
||||
- TCPget: Store incoming messages alongside the client object to keep reference
|
||||
- Merge remote-tracking branch 'upstream/master' into tcpgetfix
|
||||
- TCPget can now handle concurrent sessions (#1042)
|
||||
- Better scope handling
|
||||
- Add security checks
|
||||
- small change to udp httpadmin
|
||||
- Fix comparison to "" in tcpin
|
||||
- Change scope of clients object
|
||||
- Works when connection is left open
|
||||
- First release of multi connection tcpget
|
||||
- Fix node.error() not printing when passed false (#1037)
|
||||
- fix test for CSV array input
|
||||
- different test for Pi (rather than use serial port name)
|
||||
- Fix missing 0 handling for css node with array input
|
||||
|
||||
|
||||
#### 0.15.2: Maintenance Release
|
||||
|
||||
- Revert bidi changes to nodes and hide menu option until fixed Fixes #1024
|
||||
- Let xml node set options both ways
|
||||
- Bump serialport to use version 4
|
||||
- gpio node handle multiple bits of data returned in one go
|
||||
- HTTP In should pass application/octet-stream as buffer not string Fixes #1023
|
||||
- Handle missing httpNodeRoot setting properly
|
||||
- Config sidebar not handling node definition error properly
|
||||
- Add minimum show time to deploy spinner to avoid flicker
|
||||
- Add work-in-progress update button to palette-editor
|
||||
- Add log.removeHandler function
|
||||
- Add Crtl/Shift/p shortcut for manage palette
|
||||
- Add spinner to deploy button
|
||||
- Status messages from nodes in subflows not delegated properly Fixes #1016
|
||||
- fix spelling in join node info
|
||||
- Speed up tab scrolling
|
||||
- Update delay burst test to be more tolerant of timing Fixes #1013
|
||||
|
||||
#### 0.15.1: Maintenance Release
|
||||
|
||||
- Update default palette catalogue to use https
|
||||
- Disable palette editor if npm not found - and fix for Windows
|
||||
- Searching package catalogue should be case-insensitive Fixes #1010
|
||||
- contenteditable fields not handled in config nodes Fixes #1011
|
||||
- Change html link refs from `_new` to `_blank` to be standards compliant
|
||||
|
||||
#### 0.15.0: Milestone Release
|
||||
|
||||
Runtime
|
||||
|
||||
- Increase default apiMaxLength to 5mb and add to default settings Closes #1001
|
||||
- Add v2 /flows api and deploy-overwrite protection
|
||||
- Encrypt credentials by default
|
||||
- Ensure errors thrown by RED.events handlers don't percolate up
|
||||
|
||||
Editor
|
||||
|
||||
- Mark nodes as changed when they are moved
|
||||
- Added parent containment option for draggable. (#1006)
|
||||
- Ignore bidi event handling on non-existent and non-Input elements Closes #999
|
||||
- Remove list of flows from menu
|
||||
- Allow nodes to be imported with their credentials
|
||||
- Add workspace search option
|
||||
- Add scrollOnAdd option to editableList
|
||||
- Add swift markup to editor for open whisk node
|
||||
- Scrollable tabs 👍
|
||||
- Allow linking to individual flow via url hash
|
||||
- Avoid duplicating existing subflows on import
|
||||
- Add import-to-new-tab option
|
||||
- Add new options to export-nodes dialog
|
||||
- Stop nodes being added beyond the outer bounds of the workspace
|
||||
- Default config nodes to global scope unless in a subflow Closes #972
|
||||
- Bidi support for Text Direction and Structured Text (#961)
|
||||
- Fix jQuery selector, selecting more than one help pane/popover and displaying incorrectly. (#970)
|
||||
- Fixes removeItem not passing row data to callback. (#965)
|
||||
- Move common components and add searchBox
|
||||
- Add initial palette sidebar
|
||||
|
||||
Nodes
|
||||
|
||||
- Inject node label - show topic for timestamp mode if short
|
||||
- Let change node set type if total match
|
||||
- Clean up status on close for several core nodes.
|
||||
- Change node: re-parse JSON set value each time to avoid pass-by-ref
|
||||
- Better handle HTTP Request header capitalisation
|
||||
- Enable ES6 parsing in Function editor by default Fixes #985
|
||||
- Update debug sidebar to use RED.view.reveal to show debug nodes
|
||||
- Add full path tip to file node, And tidy up Pi node tips
|
||||
- Remove WebSocket node maxlistener warning
|
||||
- Update mqtt-broker node to use fully name-space qualified status messages
|
||||
- Let UDP node better share same port instance if required
|
||||
- Add number of units to the delay node (rate) (#994)
|
||||
- Allow http middleware to skip rawBodyParser
|
||||
- Let change node move property to sub-property.
|
||||
- Add info to exec warning about buffered output if using python
|
||||
- TCP node: pass on latest input msg properties
|
||||
- Make sure MQTT broker is really set
|
||||
- Fix escape character catch in TCPGet + support 0x?? sequences
|
||||
- Fix split character in TCP Request node
|
||||
- Add CSS highlighting to the template node (#950)
|
||||
- Only update switch previous value after all rules are run
|
||||
|
||||
Other
|
||||
|
||||
- Add npm build/test scripts Closes #946 #660
|
||||
- Move travis to node 6 and 7 - drop 5 and 0.12
|
||||
|
||||
|
||||
#### 0.14.6: Maintenance Release
|
||||
|
||||
Fixes
|
||||
|
||||
- Tell ace about Function node globals. Closes #927
|
||||
- Tidy up mqtt nodes - linting and done handling. Closes #935
|
||||
- Fix invalid html in TCP and HTML node edit templates
|
||||
- Add proper help text to link nodes
|
||||
- Handle importing old mqtt-broker configs that lack properties
|
||||
- Update ace to 1.2.4
|
||||
- Allow config nodes to provide a sort function for their select list
|
||||
- Add log warning if node module required version cannot be satisfied
|
||||
- Handle empty credentials file. Closes #937
|
||||
- Add RPi.GPIO lib test for ArchLinux
|
||||
|
||||
#### 0.14.5: Maintenance Release
|
||||
|
||||
Fixes
|
||||
|
||||
- Cannot clear cookies with http nodes
|
||||
- let HTML parse node allow msg.select set select
|
||||
- Validate nodes on import after any references have been remapped
|
||||
- Debug node handles objects without constructor property Fixes #933
|
||||
- Ensure 'false' property values are displayed in info panel Fixes #940
|
||||
- Fix node enable/disable over restart - load configs after settings init
|
||||
|
||||
#### 0.14.4: Maintenance Release
|
||||
|
||||
Nodes
|
||||
|
||||
- Update trigger node ui to use typedInputs
|
||||
- Better handling of quotes in CSV node
|
||||
- Clarify the MQTT node sends msg.payload - closes #929
|
||||
- Inject node should reuse the message it is triggered with Closes #914
|
||||
- Stop trigger node re-using old message
|
||||
- Allow node.status text to be 'falsey' values
|
||||
|
||||
Fixes
|
||||
|
||||
- Handle DOMException when embedded in an iframe of different origin Fixes #932
|
||||
- Fix double firing of menu actions
|
||||
- Fix select box handling in Safari - fixes #928
|
||||
- Clear context in node test helper Fixes #858
|
||||
- Allow node properties to be same as existing object functions Fixes #880
|
||||
- Handle comms link closing whilst completing the initial connect
|
||||
- Protect against node type names that clash with Object property names Fixes #917
|
||||
- Clone default node properties to avoid reference leakage
|
||||
- Strip tab node definition when exporting
|
||||
- Check for null config properties in editor before over-writing them
|
||||
- Add hasUsers flag to config nodes
|
||||
|
||||
Editor
|
||||
|
||||
- Add sql mode to ace editor
|
||||
- Keyboard shortcuts dialog update (#923)
|
||||
- Ensure importing link nodes to a subflow doesn't add outbound links Fixes #921
|
||||
- Add updateConfigNodeUsers function to editor
|
||||
- Scroll to bottom when item added to editableList
|
||||
- Form input widths behave more consistently when resizing Fixes #919 #920
|
||||
|
||||
#### 0.14.3: Maintenance Release
|
||||
|
||||
Fixes
|
||||
|
||||
- Create default setting.js in user-specified directory. Fixes #908
|
||||
- MQTT In subscription qos not defaulting properly
|
||||
- Let exec node handle 0 as well as "0"
|
||||
|
||||
#### 0.14.2: Maintenance Release
|
||||
|
||||
Fixes
|
||||
|
||||
- Cannot add new twitter credentials. Fixes #913
|
||||
- Support array references in Debug property field
|
||||
|
||||
#### 0.14.1: Maintenance Release
|
||||
|
||||
Fixes
|
||||
|
||||
- Handle undefined property that led to missing wires in the editor
|
||||
- Remove duplicate 'Delete' entry in keyboard shortcut window. Closes #911
|
||||
- Add 'exec' to node-red-pi launch script. Closes #910
|
||||
|
||||
#### 0.14.0: Milestone Release
|
||||
|
||||
Editor
|
||||
|
||||
- Replace edit dialog with edit tray
|
||||
- Enable shift-drag detach of just the selected link
|
||||
- Allow workspace tabs to be re-ordered
|
||||
- Scope keyboard shortcuts to dom elements
|
||||
- Ensure parent nodes marked as changed due to child config node changes
|
||||
- Validate all edit dialog inputs when one changes
|
||||
- Add editableList widget and update Switch/Change nodes to use it
|
||||
- Add option to filter Debug sidebar by flow and highlight subflow-emitting nodes
|
||||
- Back off comms reconnect attempts after prolonged failures
|
||||
- Prompt for login if comms reconnect fails authentication
|
||||
- Change style of nodes in subflow template view
|
||||
- Add CHANGELOG.md and make it accessible from menu
|
||||
|
||||
Runtime
|
||||
|
||||
- Always log node warnings on start without requiring -v
|
||||
- Add support for loading scoped node modules. Closes #885
|
||||
- Add process.env.PORT to settings.js
|
||||
- Clear node context on deploy. Closes #870
|
||||
- Enable finer grained permissions in adminAuth
|
||||
|
||||
Nodes
|
||||
|
||||
- Enable config nodes to reference other config nodes
|
||||
- Add Split/Join nodes
|
||||
- Add Link nodes
|
||||
- Add support to HTTP In node for PATCH requests. Closes #904
|
||||
- Add cookie handling to HTTP In and HTTP Response nodes
|
||||
- Add repeat indicator to inject node label. Closes #887
|
||||
- Add javascript highlighter to template node
|
||||
- Add optional timeout to exec node
|
||||
- Add TLS node and update MQTT/HTTP nodes to use it
|
||||
- Let trigger node also send last payload to arrive
|
||||
- Add timestamp as a default typedInput and update Inject and change nodes to match,
|
||||
- Add QoS option to MQTT In node
|
||||
- Add status to exec spawn mode
|
||||
- Add Move capability to Change node
|
||||
- Update Serial node to support custom baud rates
|
||||
- Add support for array-syntax in typedInput msg properties
|
||||
- Add RED.util to Function node sandbox
|
||||
- Capture error stack on node.error. Closes #879
|
||||
|
||||
|
||||
Fixes
|
||||
|
||||
- Add error handling to all node definition api calls
|
||||
- Handle null return from Function node in array of messages
|
||||
- Defer loading of token sessions until they are accessed. Fixes #895
|
||||
- set pi gpio pin status correctly if set on start
|
||||
- Prevent parent window scrolling when view is focused. Fixes #635
|
||||
- Handle missing tab nodes in a loaded flow config
|
||||
- Ensure typedInput dropdown doesn't fall off the page
|
||||
- Protect against node types with reserved names such as toString. Fixes #880
|
||||
- Do not rely on the HTML file to identify where nodes are registered from
|
||||
- Preserve node properties on import
|
||||
- Fix regression in delay node. topic based queue was emptying all the time instead of spreading out messages.
|
||||
- Throw an error if a Function node adds an input event listener
|
||||
- Fix hang on partial deploy with disconnected mqtt node
|
||||
- TypedInput: preload type icons to ensure width calc correct
|
||||
- Ensure tcp node creates a buffer of size 1 at least
|
||||
- Return editorTheme default if value is undefined
|
||||
- Fix RED.util.compareObjects for Function created objects and Buffers
|
||||
- Ensure default settings copied to command-line specified userDir
|
||||
|
||||
|
||||
#### 0.13.4: Maintenance Release
|
||||
|
||||
- Add timed release mode to delay node
|
||||
- Enable link splicing for when import_dragging nodes. Closes #811
|
||||
- Fix uncaught exception on deploy whilst node sending messages
|
||||
- Deprecate old mqtt client and connection pool modules
|
||||
- Change node: add bool/num types to change mode Closes #835
|
||||
- Validate fields that are `$(env-vars)` Closes #825
|
||||
- Handle missing config nodes when validating node properties
|
||||
- Pi node - don't try to send data if closing
|
||||
- Load node message catalog when added dynamically
|
||||
- Split palette labels on spaces and hyphens when laying out
|
||||
- Warn if editor routes are accessed but runtime not started Closes #816
|
||||
- Better handling of zero-length flow files Closes #819
|
||||
- Allow runtime calls to RED._ to specify other namespace
|
||||
- Better right alignment of numerics in delay and trigger nodes
|
||||
- Allow node modules to include example flows
|
||||
- Create node_modules in userDir
|
||||
- Ensure errors in node def functions don't break view rendering Fixes #815
|
||||
- Updated Inject node info with instructions for flow and global options
|
||||
|
||||
|
||||
|
||||
#### 0.13.3: Maintenance Release
|
||||
|
||||
- Fix crash on repeated inject of invalid json payload
|
||||
- Add binary mode to tail node
|
||||
- Revert Cheerio to somewhat smaller version
|
||||
- Add os/platform info to default debug
|
||||
|
||||
|
||||
|
||||
#### 0.13.2: Maintenance Release
|
||||
|
||||
- Don't force reconnect mqtt client if message arrives (fixes the MQTT connect/disconnect endless cycle)
|
||||
- Add -p/--port option to override listening port
|
||||
- Invert config node filter toggle button colours so state is more obvious
|
||||
- Add timeout to httprequest node
|
||||
- Tidy up of all node info content - make style consistent
|
||||
- Make jquery spinner element css consistent with other inputs
|
||||
- tcp node add reply (to all) capability
|
||||
- Allow the template node to be treated as plain text
|
||||
- Validate MQTT In topics Fixes #792
|
||||
- httpNodeAuth should not block http options requests Fixes #793
|
||||
- Disable perMessageDeflate on WS servers - fixes 'zlib binding closed' error
|
||||
- Clear trigger status icon on re-deploy
|
||||
- Don't default inject payload to blank string
|
||||
- Trigger node, add configurable reset
|
||||
- Allow function properties in settings Fixes #790 - fixes use of httpNodeMiddleware
|
||||
- Fix order of config dialog calls to save/creds/validate
|
||||
- Add debounce to Pi GPIO node
|
||||
|
||||
|
||||
|
||||
#### 0.13.1: Maintenance Release
|
||||
|
||||
- Revert wrapping of http request object
|
||||
|
||||
|
||||
|
||||
#### 0.13.0: Milestone Release
|
||||
|
||||
- Add 'previous value' option to Switch node
|
||||
- Allow existing nodes to splice into links on drag
|
||||
- CORS not properly configured on multiple http routes Fixes #783
|
||||
- Restore shift-drag to snap/unsnap to grid
|
||||
- Moving nodes with keyboard should flag workspace dirty
|
||||
- Notifications flagged as fixed should not be click-closable
|
||||
- Rework config sidebar and deploy warning
|
||||
- Wrap http request object to match http response object
|
||||
- Add 'view' menu and reorganise a few things
|
||||
- Allow shift-click to detach existing wires
|
||||
- Splice nodes dragged from palette into links
|
||||
- try to trim imported/dragged flows to [ ]
|
||||
- Move version number as title of NR logo
|
||||
- Moving nodes mark workspace as dirty
|
||||
- Ok/Cancel edit dialogs with Ctrl-Enter/Escape
|
||||
- Handle OSX Meta key when selecting nodes
|
||||
- Add grid-alignment options
|
||||
- Add oneditresize function definition
|
||||
- Rename propertySelect to typedInput and add boolean opt
|
||||
- Add propertySelect to switch node
|
||||
- Add propertySelect support to Change node
|
||||
- Add context/flow/global support to Function node
|
||||
- Add node context/flow/global
|
||||
- Add propertySelect jquery widget
|
||||
- Add add/update/delete flow apis
|
||||
- Allow core nodes dir to be provided to runtime via settings
|
||||
- Tidy up API passed to node modules
|
||||
- Move locale files under api/runtime components
|
||||
- Add flow reload admin api
|
||||
|
||||
|
||||
|
||||
#### 0.12.5: Maintenance Release
|
||||
|
||||
- Add attribute capability to HTML parser node
|
||||
- Add Pi Keyboard code node
|
||||
- Fix for MQTT client connection cycling on partial deploy
|
||||
- Fix for tcp node properly closing connections
|
||||
- Update sentiment node dependencies
|
||||
- Fix for file node handling of UTF8 extended characters
|
||||
|
||||
|
||||
|
||||
#### 0.12.4: Maintenance Release
|
||||
|
||||
- Add readOnly setting to prevent file writes in localfilesystem storage
|
||||
- Support bcrypt for httpNodeAuth
|
||||
- Pi no longer needs root workaround to access gpio
|
||||
- Fix: Input File node will not retain the file name
|
||||
|
||||
|
||||
|
||||
#### 0.12.3: Maintenance Release
|
||||
|
||||
- Fixes for TCP Get node reconnect handling
|
||||
- Clear delay node status on re-deploy
|
||||
- Update Font-Awesome to v4.5
|
||||
- Fix trigger to block properly until reset
|
||||
- Update example auth properties in settings.js
|
||||
- Ensure httpNodeAuth doesn't get applied to admin routes
|
||||
- TCP Get node not passing on existing msg properties
|
||||
|
||||
|
||||
|
||||
#### 0.12.2: Maintenance Release
|
||||
|
||||
- Enable touch-menu for links so they can be deleted
|
||||
- Allow nodes to be installed by path name
|
||||
- Fix basic authentication on httpNode/Admin/Static
|
||||
- Handle errors thrown in Function node setTimeout/Interval
|
||||
- Fix mqtt node lifecycle with partial deployments
|
||||
- Update tcp node status on reconnect after timeout
|
||||
- Debug node not handling null messages
|
||||
- Kill processes run with exec node when flows redeployed
|
||||
- Inject time spinner incrementing value incorrectly
|
||||
|
||||
|
||||
|
||||
#### 0.12.1: Maintenance Release
|
||||
|
||||
- Enable touch-menu for links so they can be deleted
|
||||
- Allow nodes to be installed by path name
|
||||
- Fix basic authentication on httpNode/Admin/Static
|
||||
|
||||
|
||||
|
||||
#### 0.12.0: Milestone Release
|
||||
|
||||
- Change/Switch rules now resize with dialog width
|
||||
- Support for node 4.x
|
||||
- Move to Express 4.x
|
||||
- Copy default settings file to user dir on start up
|
||||
- Config nodes can be scoped to a particular subflow/tab
|
||||
- Comms link tolerates <5 second breaks in connection before notifying user
|
||||
- MQTT node overhaul - add will/tls/birth message support
|
||||
- Status node - to report status events from other nodes
|
||||
- Error node can be targeted to specific other nodes
|
||||
- JSON node can encode Array types
|
||||
- Switch node regular expression rule can now be set to be case-insensitive
|
||||
- HTTP In node can accept non-UTF8 payloads - will return a Buffer when appropriate
|
||||
- Exec node configuration consistent regardless of the spawn option
|
||||
- Function node can now display status icon/text
|
||||
- CSV node can now handle arrays
|
||||
- setInterval/clearInterval add to Function node
|
||||
- Function node automatically clears all timers (setInterval/setTimeout) when the node is stopped
|
||||
|
||||
|
||||
|
||||
#### 0.11.2: Maintenance Release
|
||||
|
||||
- Allow XML parser options be set on the message
|
||||
- Add 'mobile' category to the palette (no core nodes included)
|
||||
- Allow a message catalog provide a partial translation
|
||||
- Fix HTTP Node nls message id
|
||||
- Remove delay spinner upper limit
|
||||
- Update debug node output to include length of payload
|
||||
|
||||
|
||||
|
||||
|
||||
#### 0.11.1: Maintenance Release
|
||||
|
||||
- Fix exclusive config node check when type not registered (prevented HTTP In node from being editable unless the swagger node was also installed)
|
||||
|
||||
|
||||
|
||||
#### 0.11.0: Milestone Release
|
||||
|
||||
- Add Node 0.12 support
|
||||
- Internationalization support
|
||||
- Editor UI refresh
|
||||
- Add RBE node
|
||||
- File node optionally creates path to file
|
||||
- Function node can access `clearTimeout`
|
||||
- Fix: Unable to login with 'read' permission
|
||||
|
||||
|
||||
|
||||
#### 0.10.10: Maintenance Release
|
||||
|
||||
- Fix permissions issue with packaged nrgpio script
|
||||
- Add better help message if deprecated node missing
|
||||
|
||||
|
||||
|
||||
#### 0.10.9: Maintenance Release
|
||||
|
||||
Fix packaging of bin scripts
|
||||
|
||||
|
||||
|
||||
#### 0.10.8: Maintenance Release
|
||||
|
||||
- Nodes moved out of core
|
||||
- still included as a dependency: twitter, serial, email, feedparser
|
||||
- no longer included: mongo, arduino, irc, redis
|
||||
- node icon defn can be a function
|
||||
- http_proxy support
|
||||
- httpNodeMiddleware setting
|
||||
- Trigger node ui refresh
|
||||
- editorTheme setting
|
||||
- Warn on deploy of unused config nodes
|
||||
- catch node prevents error loops
|
||||
|
||||
|
||||
|
||||
#### 0.10.6: Maintenance Release
|
||||
|
||||
Changes:
|
||||
- Performance improvements in editor
|
||||
- Palette appearance update
|
||||
- Warn on navigation with undeployed changes
|
||||
- Disable undeployed node action buttons
|
||||
- Disable subflow node action buttons
|
||||
- Add Catch node
|
||||
- Add logging functions to Function node
|
||||
- Add send function to Function node
|
||||
- Update Change node to support multiple rules
|
||||
|
||||
|
||||
|
||||
#### 0.10.4: Maintenance Release
|
||||
|
||||
Changes:
|
||||
|
||||
- http request node passes on request url as msg.url
|
||||
- handle config nodes appearing out of order in flow file - don't assume they are always at the start
|
||||
- move subflow palette category to the top, to make it more obvious
|
||||
- fix labelling of Raspberry Pi pins
|
||||
- allow email node to mark mail as read
|
||||
- fix saving library content
|
||||
- add node-red and node-red-pi start scripts
|
||||
- use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir)
|
||||
|
||||
|
||||
|
||||
#### 0.10.3: Maintenance Release
|
||||
|
||||
Fixes:
|
||||
|
||||
- httpAdminAuth was too aggressively deprecated (ie removed); restoring with a console warning when used
|
||||
- adds reporting of node.js version on start-up
|
||||
- mongo node skip/limit options can be strings or numbers
|
||||
- CSV parser passes through provided message object
|
||||
|
||||
|
||||
|
||||
#### 0.10.2: Maintenance Release
|
||||
|
||||
Fixes:
|
||||
- subflow info sidebar more useful
|
||||
- adds missing font-awesome file
|
||||
- inject node day selection defaulted to invalid selection
|
||||
- loading a flow with no tabs failed to add nodes to default tab
|
74
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@nodered.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
@@ -6,11 +6,14 @@ We welcome contributions, but request you follow these guidelines.
|
||||
- [Feature requests](#feature-requests)
|
||||
- [Pull-Requests](#pull-requests)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
|
||||
|
||||
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable
|
||||
behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
|
||||
|
||||
## Raising issues
|
||||
|
||||
Please raise any bug reports on the project's
|
||||
[issue tracker](https://github.com/node-red/node-red/issues?state=open). Be sure to
|
||||
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
||||
search the list to see if your issue has already been raised.
|
||||
|
||||
A good bug report is one that make it easy for us to understand what you were
|
||||
@@ -25,7 +28,6 @@ At a minimum, please include:
|
||||
- Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly.
|
||||
- Version of node.js - what does `node -v` say?
|
||||
|
||||
|
||||
## Feature requests
|
||||
|
||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
@@ -33,24 +35,16 @@ For feature requests, please raise them on the [mailing list](https://groups.goo
|
||||
## Pull-Requests
|
||||
|
||||
If you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
|
||||
|
||||
### Contributor License Agreement
|
||||
All contributors need to sign the JS Foundation's Contributor License Agreement.
|
||||
It is an online process and quick to do. You can read the details of the agreement
|
||||
here: https://cla.js.foundation/node-red/node-red.
|
||||
|
||||
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
|
||||
change your rights to use your own Contributions for any other purpose.
|
||||
If you raise a pull-request without having signed the CLA, you will be prompted
|
||||
to do so automatically.
|
||||
|
||||
You can download the CLAs here:
|
||||
|
||||
- [individual](http://nodered.org/cla/node-red-cla-individual.pdf)
|
||||
- [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf)
|
||||
|
||||
If you are an IBMer, please contact us directly as the contribution process is
|
||||
slightly different.
|
||||
|
||||
### Coding standards
|
||||
|
||||
@@ -59,13 +53,5 @@ code base. Some basic rules include:
|
||||
|
||||
- all files must have the Apache license in the header.
|
||||
- indent with 4-spaces, no tabs. No arguments.
|
||||
- opening brace on same line as `if`/`for`/`function`/etc, closing brace on its
|
||||
own line.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- opening brace on same line as `if`/`for`/`function` and so on, closing brace
|
||||
on its own line.
|
||||
|
520
Gruntfile.js
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2014 IBM Corp.
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,82 +14,460 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
|
||||
var nodemonArgs = ["-v"];
|
||||
var flowFile = grunt.option('flowFile');
|
||||
if (flowFile) {
|
||||
nodemonArgs.push(flowFile);
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
dist: ".dist"
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
// http://www.jshint.com/docs/options/
|
||||
"asi": true, // allow missing semicolons
|
||||
"curly": true, // require braces
|
||||
"eqnull": true, // ignore ==null
|
||||
"forin": true, // require property filtering in "for in" loops
|
||||
"immed": true, // require immediate functions to be wrapped in ( )
|
||||
"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
//"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
"loopfunc": true, // allow functions to be defined in loops
|
||||
"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/**/*.js',
|
||||
'public/red/**/*.js'
|
||||
],
|
||||
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/**/*.js' ]
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'public/red/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
files: {
|
||||
src: ['test/**/*.js']
|
||||
},
|
||||
options: {
|
||||
"expr": true
|
||||
}
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc:true
|
||||
// http://www.jshint.com/docs/options/
|
||||
//"asi": true, // allow missing semicolons
|
||||
//"curly": true, // require braces
|
||||
//"eqnull": true, // ignore ==null
|
||||
//"forin": true, // require property filtering in "for in" loops
|
||||
//"immed": true, // require immediate functions to be wrapped in ( )
|
||||
//"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
////"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
//"loopfunc": true, // allow functions to be defined in loops
|
||||
//"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/core/*/*.js',
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/core/*/*.js' ]
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'editor/js/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
files: {
|
||||
src: ['test/**/*.js']
|
||||
},
|
||||
options: {
|
||||
"expr": true
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
separator: ";",
|
||||
},
|
||||
build: {
|
||||
src: [
|
||||
// Ensure editor source files are concatenated in
|
||||
// the right order
|
||||
"editor/js/red.js",
|
||||
"editor/js/events.js",
|
||||
"editor/js/i18n.js",
|
||||
"editor/js/settings.js",
|
||||
"editor/js/user.js",
|
||||
"editor/js/comms.js",
|
||||
"editor/js/text/bidi.js",
|
||||
"editor/js/text/format.js",
|
||||
"editor/js/ui/state.js",
|
||||
"editor/js/nodes.js",
|
||||
"editor/js/history.js",
|
||||
"editor/js/validators.js",
|
||||
"editor/js/ui/utils.js",
|
||||
"editor/js/ui/common/editableList.js",
|
||||
"editor/js/ui/common/menu.js",
|
||||
"editor/js/ui/common/popover.js",
|
||||
"editor/js/ui/common/searchBox.js",
|
||||
"editor/js/ui/common/tabs.js",
|
||||
"editor/js/ui/common/typedInput.js",
|
||||
"editor/js/ui/actions.js",
|
||||
"editor/js/ui/deploy.js",
|
||||
"editor/js/ui/diff.js",
|
||||
"editor/js/ui/keyboard.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/palette-editor.js",
|
||||
"editor/js/ui/editor.js",
|
||||
"editor/js/ui/tray.js",
|
||||
"editor/js/ui/clipboard.js",
|
||||
"editor/js/ui/library.js",
|
||||
"editor/js/ui/notifications.js",
|
||||
"editor/js/ui/search.js",
|
||||
"editor/js/ui/typeSearch.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.3.min.js",
|
||||
"editor/vendor/bootstrap/js/bootstrap.min.js",
|
||||
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
|
||||
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"editor/vendor/marked/marked.min.js",
|
||||
"editor/vendor/d3/d3.v3.min.js",
|
||||
"editor/vendor/i18next/i18next.min.js"
|
||||
],
|
||||
"public/vendor/vendor.css": [
|
||||
// TODO: resolve relative resource paths in
|
||||
// bootstrap/FA/jquery
|
||||
],
|
||||
"public/vendor/jsonata/jsonata.js": [
|
||||
"node_modules/jsonata/jsonata.js",
|
||||
"editor/vendor/jsonata/formatter.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
build: {
|
||||
files: {
|
||||
'public/red/red.min.js': 'public/red/red.js',
|
||||
'public/red/main.min.js': 'public/red/main.js',
|
||||
'public/vendor/jsonata/jsonata.min.js': 'public/vendor/jsonata/jsonata.js',
|
||||
'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js',
|
||||
'public/vendor/ace/worker-jsonata.js': 'editor/vendor/jsonata/worker-jsonata.js',
|
||||
'public/vendor/ace/snippets/jsonata.js': 'editor/vendor/jsonata/snippets-jsonata.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
build: {
|
||||
options: {
|
||||
outputStyle: 'compressed'
|
||||
},
|
||||
files: [{
|
||||
dest: 'public/red/style.min.css',
|
||||
src: 'editor/sass/style.scss'
|
||||
},
|
||||
{
|
||||
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
|
||||
src: 'editor/vendor/bootstrap/css/bootstrap.css'
|
||||
}]
|
||||
}
|
||||
},
|
||||
jsonlint: {
|
||||
messages: {
|
||||
src: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
]
|
||||
},
|
||||
keymaps: {
|
||||
src: [
|
||||
'editor/js/keymap.json'
|
||||
]
|
||||
}
|
||||
},
|
||||
attachCopyright: {
|
||||
js: {
|
||||
src: [
|
||||
'public/red/red.min.js',
|
||||
'public/red/main.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: ['copy:build','concat','uglify','attachCopyright:js']
|
||||
},
|
||||
sass: {
|
||||
files: [
|
||||
'editor/sass/**/*.scss'
|
||||
],
|
||||
tasks: ['sass','attachCopyright:css']
|
||||
},
|
||||
json: {
|
||||
files: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
],
|
||||
tasks: ['jsonlint:messages']
|
||||
},
|
||||
keymaps: {
|
||||
files: [
|
||||
'editor/js/keymap.json'
|
||||
],
|
||||
tasks: ['jsonlint:keymaps','copy:build']
|
||||
},
|
||||
misc: {
|
||||
files: [
|
||||
'CHANGELOG.md'
|
||||
],
|
||||
tasks: ['copy:build']
|
||||
}
|
||||
},
|
||||
|
||||
nodemon: {
|
||||
/* uses .nodemonignore */
|
||||
dev: {
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'red','nodes'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
concurrent: {
|
||||
dev: {
|
||||
tasks: ['nodemon', 'watch'],
|
||||
options: {
|
||||
logConcurrentOutput: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
copy: {
|
||||
build: {
|
||||
files:[
|
||||
{
|
||||
src: 'editor/js/main.js',
|
||||
dest: 'public/red/main.js'
|
||||
},
|
||||
{
|
||||
src: 'editor/js/keymap.json',
|
||||
dest: 'public/red/keymap.json'
|
||||
},
|
||||
{
|
||||
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
|
||||
},
|
||||
{
|
||||
src: 'CHANGELOG.md',
|
||||
dest: 'public/red/about'
|
||||
}
|
||||
]
|
||||
},
|
||||
release: {
|
||||
files: [{
|
||||
mode: true,
|
||||
expand: true,
|
||||
src: [
|
||||
'*.md',
|
||||
'LICENSE',
|
||||
'package.json',
|
||||
'settings.js',
|
||||
'red.js',
|
||||
'lib/.gitignore',
|
||||
'nodes/*.demo',
|
||||
'nodes/core/**',
|
||||
'red/**',
|
||||
'public/**',
|
||||
'editor/templates/**',
|
||||
'bin/**'
|
||||
],
|
||||
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
|
||||
}]
|
||||
}
|
||||
},
|
||||
chmod: {
|
||||
options: {
|
||||
mode: '755'
|
||||
},
|
||||
release: {
|
||||
// Target-specific file/dir lists and/or options go here.
|
||||
src: [
|
||||
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*')
|
||||
]
|
||||
}
|
||||
},
|
||||
compress: {
|
||||
release: {
|
||||
options: {
|
||||
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
|
||||
},
|
||||
expand: true,
|
||||
cwd: '<%= paths.dist %>/',
|
||||
src: ['node-red-<%= pkg.version %>/**']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
grunt.registerTask('default', ['test-core','test-editor','test-nodes']);
|
||||
|
||||
grunt.registerTask('test-core', ['jshint:core','simplemocha:core']);
|
||||
grunt.registerTask('test-editor', ['jshint:editor']);
|
||||
grunt.registerTask('test-nodes', ['simplemocha:nodes']);
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-nodemon');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-chmod');
|
||||
grunt.loadNpmTasks('grunt-jsonlint');
|
||||
|
||||
grunt.registerMultiTask('attachCopyright', function() {
|
||||
var files = this.data.src;
|
||||
var copyright = "/**\n"+
|
||||
" * Copyright JS Foundation and other contributors, http://js.foundation\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','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']);
|
||||
|
||||
grunt.registerTask('dev',
|
||||
'Developer mode: run node-red, watch for source changes and build/restart',
|
||||
['build','setDevEnv','concurrent:dev']);
|
||||
|
||||
grunt.registerTask('release',
|
||||
'Create distribution zip file',
|
||||
['build','clean:release','copy:release','chmod:release','compress:release']);
|
||||
|
||||
};
|
||||
|
57
INSTALL.md
@@ -1,57 +0,0 @@
|
||||
Node-RED Install
|
||||
================
|
||||
|
||||
## Install node.js
|
||||
|
||||
You can get the latest version from <http://nodejs.org/download/>.
|
||||
|
||||
Or, you may want to use a version from your operating system's package manager:
|
||||
<https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager>
|
||||
|
||||
## Get Node-RED
|
||||
|
||||
Clone the repository from GitHub:
|
||||
|
||||
$ git clone git@github.com:node-red/node-red.git
|
||||
|
||||
## Install the pre-requisite modules
|
||||
|
||||
From the top-level directory of Node-RED, run:
|
||||
|
||||
$ npm install
|
||||
|
||||
This will install the core pre-requisite modules.
|
||||
|
||||
## Run Node-RED
|
||||
|
||||
From the top-level directory, run:
|
||||
|
||||
$ node red.js
|
||||
|
||||
You can then access Node-RED at <http://localhost:1880>.
|
||||
|
||||
Online documentation is available at <http://nodered.org/docs>.
|
||||
|
||||
## Installing individual node dependencies
|
||||
|
||||
When Node-RED starts, it attempts to load the nodes from the `nodes/` directory.
|
||||
Each will have its own set of dependencies that will need to be installed before
|
||||
the node is available in the palette.
|
||||
|
||||
To help identify the dependencies, Node-RED logs any modules it fails to find
|
||||
for a particular node. You don't have to install these unless you want or need
|
||||
that node to appear.
|
||||
|
||||
Alternatively, a node's `.js` file can be examined to identify the modules it
|
||||
explicitly requires. For example, the Twitter node is defined in
|
||||
`nodes/social/27-twitter.js` and contains:
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var ntwitter = require('ntwitter');
|
||||
var OAuth= require('oauth').OAuth;
|
||||
|
||||
Of these, `ntwitter` and `oauth` are neither built-in modules nor ones provided
|
||||
by Node-RED itself. They can subsequently be installed by running:
|
||||
|
||||
$ npm install ntwitter oauth
|
||||
|
1
LICENSE
@@ -1,3 +1,4 @@
|
||||
Copyright JS Foundation and other contributors, http://js.foundation
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
60
README.md
@@ -2,49 +2,73 @@
|
||||
|
||||
http://nodered.org
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red) [](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||

|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
Check out [INSTALL](INSTALL.md) for full instructions on getting started.
|
||||
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
started.
|
||||
|
||||
1. download the zip and unzip, or git clone
|
||||
2. cd node-red
|
||||
3. npm install
|
||||
4. node red.js
|
||||
5. Open <http://localhost:1880>
|
||||
1. `sudo npm install -g node-red`
|
||||
2. `node-red`
|
||||
3. Open <http://localhost:1880>
|
||||
|
||||
## Getting Help
|
||||
|
||||
More documentation can be found [here](http://nodered.org/docs).
|
||||
|
||||
For further help, or general discussion, please use the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
For further help, or general discussion, please use the
|
||||
[mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
|
||||
## Browser Support
|
||||
## Developers
|
||||
|
||||
The Node-RED editor runs in the browser. We routinely develop and test using
|
||||
Chrome and Firefox. We have anecdotal evidence that it works in recent versions of IE.
|
||||
If you want to run the latest code from git, here's how to get started:
|
||||
|
||||
We have basic support for using in mobile/tablet browsers.
|
||||
1. Clone the code:
|
||||
|
||||
git clone https://github.com/node-red/node-red.git
|
||||
cd node-red
|
||||
|
||||
2. Install the node-red dependencies
|
||||
|
||||
npm install
|
||||
|
||||
3. Build the code
|
||||
|
||||
npm run build
|
||||
|
||||
4. Run
|
||||
|
||||
npm start
|
||||
or
|
||||
|
||||
node red.js
|
||||
|
||||
## Contributing
|
||||
|
||||
Before raising a pull-request, please read our [contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
Before raising a pull-request, please read our
|
||||
[contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
|
||||
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable
|
||||
behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
|
||||
|
||||
## Authors
|
||||
|
||||
Node-RED is a creation of [IBM Emerging Technology](http://ibm.com/blogs/et).
|
||||
Node-RED is a project of the [JS Foundation](http://js.foundation).
|
||||
|
||||
It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
|
||||
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
|
||||
For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013, 2015 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE).
|
||||
|
43
bin/node-red-pi
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright JS Foundation and other contributors, http://js.foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# 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
|
||||
exec /usr/bin/env node $OPTIONS $SCRIPT_PATH/../red.js $ARGS
|
BIN
editor/favicon.ico
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
editor/icons/bridge-dash.png
Normal file
After Width: | Height: | Size: 508 B |
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 575 B |
BIN
editor/icons/cog.png
Normal file
After Width: | Height: | Size: 493 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 |
BIN
editor/icons/join.png
Normal file
After Width: | Height: | Size: 253 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 |
BIN
editor/icons/link-out.png
Normal file
After Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 671 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
BIN
editor/icons/parser-csv.png
Normal file
After Width: | Height: | Size: 413 B |
BIN
editor/icons/parser-html.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
editor/icons/parser-json.png
Normal file
After Width: | Height: | Size: 467 B |
BIN
editor/icons/parser-xml.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
editor/icons/parser-yaml.png
Normal file
After Width: | Height: | Size: 423 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/split.png
Normal file
After Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 509 B |
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B |
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 392 B |
BIN
editor/images/grip.png
Normal file
After Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
9
editor/images/node-red-icon-black.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="480" height="480" viewBox="0, 0, 480, 480">
|
||||
<g id="Calque_1">
|
||||
<path d="M408,16 C438.928,16 464,41.072 464,72 L464,408 C464,438.928 438.928,464 408,464 L72,464 C41.072,464 16,438.928 16,408 L16,296.002 L69.339,296.002 C89.715,296.002 105.993,279.497 105.993,259.12 L105.992,242.19 C190.296,243.397 214.83,265.317 241.661,288.777 C267.502,311.375 296.48,335.662 371.989,336.261 L371.993,344.57 C372.002,364.947 388.693,382 409.069,382 L463.991,382 L463.991,356 L409.069,356 C402.189,356 396.991,351.449 396.991,344.569 L396.991,308.32 C396.991,301.44 402.189,296 409.069,296 L463.991,296 L463.991,272 L409.069,272 C388.693,272 372.002,287.943 371.993,308.32 L371.99,316.908 C300.63,316.672 280.362,296.883 254.41,274.189 C232.267,254.825 206.244,233.534 148.914,225.789 C149.412,225.361 149.872,224.945 150.353,224.505 C161.391,214.382 167.343,202.153 173.167,191.593 C178.99,181.034 184.469,172.221 193.444,166.061 C200.725,161.064 211.08,157.338 226.992,156.647 L226.993,165.123 C226.997,185.5 243.431,202.999 263.808,202.999 L411.141,202.999 C431.517,202.999 447.993,185.5 447.993,165.123 L447.993,128.874 C447.993,108.497 431.517,91.999 411.141,91.999 L263.808,91.999 C243.431,91.999 226.983,108.496 226.993,128.874 L226.998,137.281 C207.794,138.053 193.238,142.713 182.496,150.086 C169.469,159.028 162.277,171.247 156.21,182.247 C150.144,193.247 145.009,203.104 137.25,210.218 C130.497,216.411 121.157,221.193 105.993,222.976 L105.993,222.579 C106.111,202.203 89.715,186.002 69.339,186.002 L16,186.002 L16,72 C16,41.072 41.072,16 72,16 L408,16 z" fill="#000000"/>
|
||||
<path d="M16,211.002 L69.339,211.002 C76.219,211.002 81.992,215.991 81.992,222.871 L81.992,259.12 C81.992,266 76.219,272.002 69.339,272.002 L16,272.002 L16,211.002 z" fill="#000000"/>
|
||||
<path d="M411.135,116.997 C418.015,116.997 422.987,121.992 422.987,128.872 L422.987,165.122 C422.987,172.002 418.015,176.998 411.135,176.998 L263.802,176.998 C256.923,176.998 250.99,172.002 250.99,165.122 L250.99,128.872 C250.99,121.993 256.923,116.997 263.802,116.997 L411.135,116.997 z" fill="#000000"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
33
editor/images/node-red-icon.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="480" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 480.00002 479.99999">
|
||||
<title>Node-RED Icon</title>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title>Node-RED Icon</dc:title>
|
||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by/3.0/"/>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Nick O'Leary</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
<cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/">
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="translate(0 -572.36)">
|
||||
<rect style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry="56" height="448" width="448" y="588.36" x="16" fill="#8f0000"/>
|
||||
<g transform="matrix(8.545 0 0 8.545 -786.19 -1949.8)">
|
||||
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m104.41 321.21c0.0138-2.3846-1.905-4.2806-4.2896-4.2806h-6.243v2.9257h6.243c0.80513 0 1.4808 0.58383 1.4808 1.389v4.2422c0 0.80513-0.67566 1.5075-1.4808 1.5075h-6.243v2.8086h6.243c2.3846 0 4.2895-1.9315 4.2895-4.3162l-0.00005-1.9812c9.8659 0.14125 12.737 2.7065 15.877 5.4519 3.0241 2.6446 6.4153 5.4869 15.252 5.557l0.00046 0.97238c0.001 2.3846 1.9543 4.3803 4.3389 4.3803h6.4273v-3.0427h-6.4273c-0.80514 0-1.4135-0.53255-1.4135-1.3377v-4.2422c0-0.80513 0.60835-1.4418 1.4135-1.4418h6.4273v-2.8086h-6.4273c-2.3846 0-4.3379 1.8658-4.3389 4.2504l-0.00045 1.005c-8.351-0.0276-10.723-2.3434-13.76-4.9992-2.5914-2.2662-5.6368-4.7578-12.346-5.6642 0.0583-0.0501 0.11211-0.0987 0.16838-0.15027 1.2918-1.1846 1.9884-2.6158 2.6699-3.8516 0.68148-1.2357 1.3227-2.267 2.373-2.9879 0.85207-0.58483 2.0639-1.0208 3.926-1.1017l0.00018 0.99192c0.00043 2.3846 1.9236 4.4325 4.3083 4.4325h17.242c2.3846 0 4.3127-2.0479 4.3127-4.4325v-4.2422c0-2.3846-1.9281-4.3153-4.3127-4.3153h-17.242c-2.3846 0-4.3095 1.9306-4.3083 4.3153l0.00051 0.98395c-2.2474 0.0903-3.9508 0.6357-5.2079 1.4985-1.5245 1.0464-2.3662 2.4764-3.0762 3.7637-0.70992 1.2873-1.3108 2.4408-2.2188 3.2734-0.79034 0.72475-1.8834 1.2844-3.658 1.493zm18.468-12.356h17.242c0.80514 0 1.387 0.58455 1.387 1.3897v4.2422c0 0.80514-0.5819 1.3898-1.387 1.3898h-17.242c-0.80514 0-1.4994-0.58462-1.4994-1.3898v-4.2422c0-0.80513 0.69431-1.3897 1.4994-1.3897z" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 1019 B |
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
BIN
editor/images/subflow_tab.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
editor/images/typedInput/09.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/az.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
editor/images/typedInput/bool.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
editor/images/typedInput/expr.png
Normal file
After Width: | Height: | Size: 563 B |
BIN
editor/images/typedInput/json.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
editor/images/typedInput/re.png
Normal file
After Width: | Height: | Size: 502 B |
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,23 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.comms = (function() {
|
||||
|
||||
|
||||
var errornotification = null;
|
||||
var clearErrorTimer = null;
|
||||
|
||||
var connectCountdownTimer = null;
|
||||
var connectCountdown = 10;
|
||||
var subscriptions = {};
|
||||
var ws;
|
||||
var pendingAuth = false;
|
||||
|
||||
var reconnectAttempts = 0;
|
||||
var active = false;
|
||||
|
||||
function connectWS() {
|
||||
var path = location.hostname+":"+location.port+document.location.pathname;
|
||||
active = true;
|
||||
var path = location.hostname;
|
||||
var port = location.port;
|
||||
if (port.length !== 0) {
|
||||
path = path+":"+port;
|
||||
}
|
||||
path = path+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
|
||||
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
pendingAuth = (auth_tokens!=null);
|
||||
|
||||
|
||||
function completeConnection() {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
@@ -37,9 +47,10 @@ RED.comms = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
reconnectAttempts = 0;
|
||||
if (errornotification) {
|
||||
clearErrorTimer = setTimeout(function() {
|
||||
errornotification.close();
|
||||
@@ -54,9 +65,17 @@ RED.comms = (function() {
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var msg = JSON.parse(event.data);
|
||||
if (pendingAuth && msg.auth == "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
if (pendingAuth && msg.auth) {
|
||||
if (msg.auth === "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
} else if (msg.auth === "fail") {
|
||||
// anything else is an error...
|
||||
active = false;
|
||||
RED.user.login({updateMenu:true},function() {
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
} else if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
@@ -74,16 +93,45 @@ RED.comms = (function() {
|
||||
}
|
||||
};
|
||||
ws.onclose = function() {
|
||||
if (errornotification == null) {
|
||||
errornotification = RED.notify("<b>Error</b>: Lost connection to server","error",true);
|
||||
} else if (clearErrorTimer) {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
if (clearErrorTimer) {
|
||||
clearTimeout(clearErrorTimer);
|
||||
clearErrorTimer = null;
|
||||
}
|
||||
setTimeout(connectWS,1000);
|
||||
reconnectAttempts++;
|
||||
if (reconnectAttempts < 10) {
|
||||
setTimeout(connectWS,1000);
|
||||
if (reconnectAttempts > 5 && errornotification == null) {
|
||||
errornotification = RED.notify(RED._("notification.errors.lostConnection"),"error",true);
|
||||
}
|
||||
} else if (reconnectAttempts < 20) {
|
||||
setTimeout(connectWS,2000);
|
||||
} else {
|
||||
connectCountdown = 60;
|
||||
connectCountdownTimer = setInterval(function() {
|
||||
connectCountdown--;
|
||||
if (connectCountdown === 0) {
|
||||
errornotification.update(RED._("notification.errors.lostConnection"));
|
||||
clearInterval(connectCountdownTimer);
|
||||
connectWS();
|
||||
} else {
|
||||
var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>';
|
||||
errornotification.update(msg);
|
||||
$(errornotification).find("a").click(function(e) {
|
||||
e.preventDefault();
|
||||
errornotification.update(RED._("notification.errors.lostConnection"));
|
||||
clearInterval(connectCountdownTimer);
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
},1000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function subscribe(topic,callback) {
|
||||
if (subscriptions[topic] == null) {
|
||||
subscriptions[topic] = [];
|
||||
@@ -93,7 +141,7 @@ RED.comms = (function() {
|
||||
ws.send(JSON.stringify({subscribe:topic}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function unsubscribe(topic,callback) {
|
||||
if (subscriptions[topic]) {
|
||||
for (var i=0;i<subscriptions[topic].length;i++) {
|
||||
@@ -107,7 +155,7 @@ RED.comms = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
connect: connectWS,
|
||||
subscribe: subscribe,
|
53
editor/js/events.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.events = (function() {
|
||||
var handlers = {};
|
||||
|
||||
function on(evt,func) {
|
||||
handlers[evt] = handlers[evt]||[];
|
||||
handlers[evt].push(func);
|
||||
}
|
||||
function off(evt,func) {
|
||||
var handler = handlers[evt];
|
||||
if (handler) {
|
||||
for (var i=0;i<handler.length;i++) {
|
||||
if (handler[i] === func) {
|
||||
handler.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function emit(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
try {
|
||||
handlers[evt][i](arg);
|
||||
} catch(err) {
|
||||
console.log("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return {
|
||||
on: on,
|
||||
off: off,
|
||||
emit: emit
|
||||
}
|
||||
})();
|
327
editor/js/history.js
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.history = (function() {
|
||||
var undo_history = [];
|
||||
|
||||
function undoEvent(ev) {
|
||||
var i;
|
||||
var len;
|
||||
var node;
|
||||
var subflow;
|
||||
var modifiedTabs = {};
|
||||
if (ev) {
|
||||
if (ev.t == 'multi') {
|
||||
len = ev.events.length;
|
||||
for (i=len-1;i>=0;i--) {
|
||||
undoEvent(ev.events[i]);
|
||||
}
|
||||
} else if (ev.t == 'replace') {
|
||||
RED.nodes.clear();
|
||||
var imported = RED.nodes.import(ev.config);
|
||||
imported[0].forEach(function(n) {
|
||||
if (ev.changed[n.id]) {
|
||||
n.changed = true;
|
||||
}
|
||||
})
|
||||
|
||||
RED.nodes.version(ev.rev);
|
||||
} else 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]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.instances) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('changed')) {
|
||||
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||
if (subflow) {
|
||||
subflow.changed = ev.subflow.changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.addWorkspace(ev.workspaces[i]);
|
||||
RED.workspaces.add(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow && ev.subflow.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow.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 (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
n.inputs = subflow.in.length;
|
||||
n.outputs = subflow.out.length;
|
||||
while (n.outputs > n.ports.length) {
|
||||
n.ports.push(n.ports.length);
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
});
|
||||
}
|
||||
if (ev.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.add(ev.nodes[i]);
|
||||
modifiedTabs[ev.nodes[i].z] = true;
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.changes) {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
node = RED.nodes.node(i);
|
||||
if (node) {
|
||||
for (var d in ev.changes[i]) {
|
||||
if (ev.changes[i].hasOwnProperty(d)) {
|
||||
node[d] = ev.changes[i][d];
|
||||
}
|
||||
}
|
||||
node.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ev.t == "move") {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
var n = ev.nodes[i];
|
||||
n.n.x = n.ox;
|
||||
n.n.y = n.oy;
|
||||
n.n.dirty = true;
|
||||
n.n.changed = n.changed;
|
||||
}
|
||||
// A move could have caused a link splice
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
if (ev.node._def.defaults[i].type) {
|
||||
// This is a config node property
|
||||
var currentConfigNode = RED.nodes.node(ev.node[i]);
|
||||
if (currentConfigNode) {
|
||||
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
|
||||
}
|
||||
var newConfigNode = RED.nodes.node(ev.changes[i]);
|
||||
if (newConfigNode) {
|
||||
newConfigNode.users.push(ev.node);
|
||||
}
|
||||
}
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
} else {
|
||||
var outputMap;
|
||||
if (ev.outputMap) {
|
||||
outputMap = {};
|
||||
for (var port in ev.outputMap) {
|
||||
if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== -1) {
|
||||
outputMap[ev.outputMap[port]] = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.editor.updateNodeProperties(ev.node,outputMap);
|
||||
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.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.subflow);
|
||||
RED.workspaces.remove(ev.subflow.subflow);
|
||||
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "reorder") {
|
||||
if (ev.order) {
|
||||
RED.workspaces.order(ev.order);
|
||||
}
|
||||
}
|
||||
Object.keys(modifiedTabs).forEach(function(id) {
|
||||
var subflow = RED.nodes.subflow(id);
|
||||
if (subflow) {
|
||||
RED.editor.validateNode(subflow);
|
||||
}
|
||||
});
|
||||
|
||||
RED.nodes.dirty(ev.dirty);
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
list: function() {
|
||||
return undo_history
|
||||
},
|
||||
depth: function() {
|
||||
return undo_history.length;
|
||||
},
|
||||
push: function(ev) {
|
||||
undo_history.push(ev);
|
||||
},
|
||||
pop: function() {
|
||||
var ev = undo_history.pop();
|
||||
undoEvent(ev);
|
||||
},
|
||||
peek: function() {
|
||||
return undo_history[undo_history.length-1];
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
43
editor/js/i18n.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.i18n = (function() {
|
||||
|
||||
return {
|
||||
init: function(done) {
|
||||
i18n.init({
|
||||
resGetPath: 'locales/__ns__',
|
||||
dynamicLoad: false,
|
||||
load:'current',
|
||||
ns: {
|
||||
namespaces: ["editor","node-red","jsonata","infotips"],
|
||||
defaultNs: "editor"
|
||||
},
|
||||
fallbackLng: ['en-US'],
|
||||
useCookie: false
|
||||
},function() {
|
||||
done();
|
||||
});
|
||||
RED["_"] = function() {
|
||||
return i18n.t.apply(null,arguments);
|
||||
}
|
||||
|
||||
},
|
||||
loadCatalog: function(namespace,done) {
|
||||
i18n.loadNamespace(namespace,done);
|
||||
}
|
||||
}
|
||||
})();
|
38
editor/js/keymap.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"*": {
|
||||
"ctrl-shift-p":"core:manage-palette",
|
||||
"ctrl-f": "core:search",
|
||||
"ctrl-=": "core:zoom-in",
|
||||
"ctrl--": "core:zoom-out",
|
||||
"ctrl-0": "core:zoom-reset",
|
||||
"ctrl-enter": "core:confirm-edit-tray",
|
||||
"ctrl-escape": "core:cancel-edit-tray",
|
||||
"ctrl-g i": "core:show-info-tab",
|
||||
"ctrl-g d": "core:show-debug-tab",
|
||||
"ctrl-g c": "core:show-config-tab",
|
||||
"ctrl-e": "core:show-export-dialog",
|
||||
"ctrl-i": "core:show-import-dialog",
|
||||
"ctrl-space": "core:toggle-sidebar"
|
||||
},
|
||||
"workspace": {
|
||||
"backspace": "core:delete-selection",
|
||||
"delete": "core:delete-selection",
|
||||
"enter": "core:edit-selected-node",
|
||||
"ctrl-c": "core:copy-selection-to-internal-clipboard",
|
||||
"ctrl-x": "core:cut-selection-to-internal-clipboard",
|
||||
"ctrl-v": "core:paste-from-internal-clipboard",
|
||||
"ctrl-z": "core:undo",
|
||||
"ctrl-a": "core:select-all-nodes",
|
||||
"shift-?": "core:show-help",
|
||||
"up": "core:move-selection-up",
|
||||
"right": "core:move-selection-right",
|
||||
"down": "core:move-selection-down",
|
||||
"left": "core:move-selection-left",
|
||||
"shift-up": "core:step-selection-up",
|
||||
"shift-right": "core:step-selection-right",
|
||||
"shift-down": "core:step-selection-down",
|
||||
"shift-left": "core:step-selection-left",
|
||||
"ctrl-shift-j": "core:show-previous-tab",
|
||||
"ctrl-shift-k": "core:show-next-tab"
|
||||
}
|
||||
}
|
274
editor/js/main.js
Normal file
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function() {
|
||||
|
||||
function loadNodeList() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
RED.nodes.setNodeList(data);
|
||||
|
||||
var nsCount = 0;
|
||||
for (var i=0;i<data.length;i++) {
|
||||
var ns = data[i];
|
||||
if (ns.module != "node-red") {
|
||||
nsCount++;
|
||||
RED.i18n.loadCatalog(ns.id, function() {
|
||||
nsCount--;
|
||||
if (nsCount === 0) {
|
||||
loadNodes();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (nsCount === 0) {
|
||||
loadNodes();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadNodes() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"text/html"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
$("body").append(data);
|
||||
$("body").i18n();
|
||||
$("#palette > .palette-spinner").hide();
|
||||
$(".palette-scroll").removeClass("hide");
|
||||
$("#palette-search").removeClass("hide");
|
||||
loadFlows();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadFlows() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json",
|
||||
},
|
||||
cache: false,
|
||||
url: 'flows',
|
||||
success: function(nodes) {
|
||||
var currentHash = window.location.hash;
|
||||
RED.nodes.version(nodes.rev);
|
||||
RED.nodes.import(nodes.flows);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
if (/^#flow\/.+$/.test(currentHash)) {
|
||||
RED.workspaces.show(currentHash.substring(6));
|
||||
}
|
||||
|
||||
var persistentNotifications = {};
|
||||
RED.comms.subscribe("notification/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var notificationId = parts[1];
|
||||
if (msg.text) {
|
||||
var text = RED._(msg.text,{default:msg.text});
|
||||
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
||||
persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout);
|
||||
} else {
|
||||
persistentNotifications[notificationId].update(text,msg.timeout);
|
||||
}
|
||||
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
|
||||
persistentNotifications[notificationId].close();
|
||||
delete persistentNotifications[notificationId];
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("status/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var node = RED.nodes.node(parts[1]);
|
||||
if (node) {
|
||||
if (msg.hasOwnProperty("text")) {
|
||||
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
|
||||
}
|
||||
node.status = msg;
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("node/#",function(topic,msg) {
|
||||
var i,m;
|
||||
var typeList;
|
||||
var info;
|
||||
if (topic == "node/added") {
|
||||
var addedTypes = [];
|
||||
msg.forEach(function(m) {
|
||||
var id = m.id;
|
||||
RED.nodes.addNodeSet(m);
|
||||
addedTypes = addedTypes.concat(m.types);
|
||||
RED.i18n.loadCatalog(id, function() {
|
||||
$.get('nodes/'+id, function(data) {
|
||||
$("body").append(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
if (addedTypes.length) {
|
||||
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
|
||||
}
|
||||
} else if (topic == "node/removed") {
|
||||
for (i=0;i<msg.length;i++) {
|
||||
m = msg[i];
|
||||
info = RED.nodes.removeNodeSet(m.id);
|
||||
if (info.added) {
|
||||
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
||||
}
|
||||
}
|
||||
} else if (topic == "node/enabled") {
|
||||
if (msg.types) {
|
||||
info = RED.nodes.getNodeSet(msg.id);
|
||||
if (info.added) {
|
||||
RED.nodes.enableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
|
||||
} else {
|
||||
$.get('nodes/'+msg.id, function(data) {
|
||||
$("body").append(data);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (topic == "node/disabled") {
|
||||
if (msg.types) {
|
||||
RED.nodes.disableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||
}
|
||||
}
|
||||
// Refresh flow library to ensure any examples are updated
|
||||
RED.library.loadFlowLibrary();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showAbout() {
|
||||
$.get('red/about', function(data) {
|
||||
var aboutHeader = '<div style="text-align:center;">'+
|
||||
'<img width="50px" src="red/images/node-red-icon.svg" />'+
|
||||
'</div>';
|
||||
|
||||
RED.sidebar.info.set(aboutHeader+marked(data));
|
||||
RED.sidebar.info.show();
|
||||
});
|
||||
}
|
||||
|
||||
function loadEditor() {
|
||||
var menuOptions = [];
|
||||
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||
{id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
|
||||
{id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
|
||||
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true},
|
||||
null,
|
||||
// {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[
|
||||
// {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}},
|
||||
// {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("ltr")}}},
|
||||
// {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("rtl")}}},
|
||||
// {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}}
|
||||
// ]},
|
||||
// null,
|
||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true}
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[
|
||||
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"},
|
||||
{id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
|
||||
]});
|
||||
menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[
|
||||
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:show-export-dialog"},
|
||||
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"}
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
|
||||
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
||||
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
|
||||
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
|
||||
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
|
||||
]});
|
||||
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"},
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"},
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
if (RED.settings.theme('palette.editable') !== false) {
|
||||
RED.palette.editor.init();
|
||||
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
|
||||
menuOptions.push(null);
|
||||
}
|
||||
|
||||
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
|
||||
menuOptions.push({id:"menu-item-show-tips",label:RED._("menu.label.showTips"),toggle:true,selected:true,onselect:"core:toggle-show-tips"});
|
||||
menuOptions.push({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")
|
||||
});
|
||||
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
|
||||
|
||||
|
||||
RED.user.init();
|
||||
RED.library.init();
|
||||
RED.palette.init();
|
||||
RED.sidebar.init();
|
||||
RED.subflow.init();
|
||||
RED.workspaces.init();
|
||||
RED.clipboard.init();
|
||||
RED.search.init();
|
||||
RED.view.init();
|
||||
RED.editor.init();
|
||||
RED.keyboard.init();
|
||||
RED.diff.init();
|
||||
|
||||
RED.menu.init({id:"btn-sidemenu",options: menuOptions});
|
||||
|
||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||
|
||||
RED.actions.add("core:show-about", showAbout);
|
||||
|
||||
RED.comms.connect();
|
||||
|
||||
$("#main-container").show();
|
||||
$(".header-toolbar").show();
|
||||
|
||||
|
||||
loadNodeList();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
||||
document.title = document.title+" : "+window.location.hostname;
|
||||
}
|
||||
|
||||
ace.require("ace/ext/language_tools");
|
||||
|
||||
RED.i18n.init(function() {
|
||||
RED.settings.init(loadEditor);
|
||||
})
|
||||
});
|
||||
})();
|
1273
editor/js/nodes.js
Normal file
16
editor/js/red.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var RED = {};
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM, Antoine Aflalo
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
|
||||
RED.settings = (function () {
|
||||
|
||||
|
||||
var loadedSettings = {};
|
||||
|
||||
|
||||
var hasLocalStorage = function () {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
@@ -33,6 +33,7 @@ RED.settings = (function () {
|
||||
}
|
||||
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
|
||||
@@ -68,23 +69,30 @@ RED.settings = (function () {
|
||||
};
|
||||
|
||||
var init = function (done) {
|
||||
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
|
||||
if (accessTokenMatch) {
|
||||
var accessToken = accessTokenMatch[1];
|
||||
RED.settings.set("auth-tokens",{access_token: accessToken});
|
||||
window.location.search = "";
|
||||
}
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(jqXHR,settings) {
|
||||
// Only attach auth header for requests to relative paths
|
||||
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
if (auth_tokens) {
|
||||
jqXHR.setRequestHeader("authorization","bearer "+auth_tokens.access_token);
|
||||
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);
|
||||
}
|
||||
jqXHR.setRequestHeader("Node-RED-API-Version","v2");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
load(done);
|
||||
}
|
||||
|
||||
|
||||
var load = function(done) {
|
||||
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
@@ -102,21 +110,43 @@ RED.settings = (function () {
|
||||
},
|
||||
error: function(jqXHR,textStatus,errorThrown) {
|
||||
if (jqXHR.status === 401) {
|
||||
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
|
||||
window.location.search = "";
|
||||
}
|
||||
RED.user.login(function() { load(done); });
|
||||
} else {
|
||||
console.log("Unexpected error:",jqXHR.status,textStatus);
|
||||
}
|
||||
} 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]];
|
||||
}
|
||||
if (v === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return v;
|
||||
} catch(err) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
load: load,
|
||||
set: set,
|
||||
get: get,
|
||||
remove: remove
|
||||
remove: remove,
|
||||
theme: theme
|
||||
}
|
||||
})
|
||||
();
|
130
editor/js/text/bidi.js
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.text = {};
|
||||
RED.text.bidi = (function() {
|
||||
var textDir = "";
|
||||
var LRE = "\u202A",
|
||||
RLE = "\u202B",
|
||||
PDF = "\u202C";
|
||||
|
||||
function isRTLValue(stringValue) {
|
||||
var length = stringValue.length;
|
||||
for (var i=0;i<length;i++) {
|
||||
if (isBidiChar(stringValue.charCodeAt(i))) {
|
||||
return true;
|
||||
}
|
||||
else if(isLatinChar(stringValue.charCodeAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isBidiChar(c) {
|
||||
return (c >= 0x05d0 && c <= 0x05ff)||
|
||||
(c >= 0x0600 && c <= 0x065f)||
|
||||
(c >= 0x066a && c <= 0x06ef)||
|
||||
(c >= 0x06fa && c <= 0x07ff)||
|
||||
(c >= 0xfb1d && c <= 0xfdff)||
|
||||
(c >= 0xfe70 && c <= 0xfefc);
|
||||
}
|
||||
|
||||
function isLatinChar(c){
|
||||
return (c > 64 && c < 91)||(c > 96 && c < 123)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the text direction of a given string.
|
||||
* @param value - the string
|
||||
*/
|
||||
function resolveBaseTextDir(value) {
|
||||
if (textDir == "auto") {
|
||||
if (isRTLValue(value)) {
|
||||
return "rtl";
|
||||
} else {
|
||||
return "ltr";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return textDir;
|
||||
}
|
||||
}
|
||||
|
||||
function onInputChange() {
|
||||
$(this).attr("dir", resolveBaseTextDir($(this).val()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds event listeners to the Input to ensure its text-direction attribute
|
||||
* is properly set based on its content.
|
||||
* @param input - the input field
|
||||
*/
|
||||
function prepareInput(input) {
|
||||
input.on("keyup",onInputChange).on("paste",onInputChange).on("cut",onInputChange);
|
||||
// Set the initial text direction
|
||||
onInputChange.call(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the text direction of a given string by adding
|
||||
* UCC (Unicode Control Characters)
|
||||
* @param value - the string
|
||||
*/
|
||||
function enforceTextDirectionWithUCC(value) {
|
||||
if (value) {
|
||||
var dir = resolveBaseTextDir(value);
|
||||
if (dir == "ltr") {
|
||||
return LRE + value + PDF;
|
||||
}
|
||||
else if (dir == "rtl") {
|
||||
return RLE + value + PDF;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the text direction for all the spans with style bidiAware under
|
||||
* workspace or sidebar div
|
||||
*/
|
||||
function enforceTextDirectionOnPage() {
|
||||
$("#workspace").find('span.bidiAware').each(function() {
|
||||
$(this).attr("dir", resolveBaseTextDir($(this).html()));
|
||||
});
|
||||
$("#sidebar").find('span.bidiAware').each(function() {
|
||||
$(this).attr("dir", resolveBaseTextDir($(this).text()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text direction preference
|
||||
* @param dir - the text direction preference
|
||||
*/
|
||||
function setTextDirection(dir) {
|
||||
textDir = dir;
|
||||
RED.nodes.eachNode(function(n) { n.dirty = true;});
|
||||
RED.view.redraw();
|
||||
RED.palette.refresh();
|
||||
enforceTextDirectionOnPage();
|
||||
}
|
||||
|
||||
return {
|
||||
setTextDirection: setTextDirection,
|
||||
enforceTextDirectionWithUCC: enforceTextDirectionWithUCC,
|
||||
resolveBaseTextDir: resolveBaseTextDir,
|
||||
prepareInput: prepareInput
|
||||
}
|
||||
})();
|
1330
editor/js/text/format.js
Normal file
35
editor/js/ui/actions.js
Normal file
@@ -0,0 +1,35 @@
|
||||
RED.actions = (function() {
|
||||
var actions = {
|
||||
|
||||
}
|
||||
|
||||
function addAction(name,handler) {
|
||||
actions[name] = handler;
|
||||
}
|
||||
function removeAction(name) {
|
||||
delete actions[name];
|
||||
}
|
||||
function getAction(name) {
|
||||
return actions[name];
|
||||
}
|
||||
function invokeAction(name) {
|
||||
if (actions.hasOwnProperty(name)) {
|
||||
actions[name]();
|
||||
}
|
||||
}
|
||||
function listActions() {
|
||||
var result = [];
|
||||
Object.keys(actions).forEach(function(action) {
|
||||
var shortcut = RED.keyboard.getShortcut(action);
|
||||
result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined})
|
||||
})
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
add: addAction,
|
||||
remove: removeAction,
|
||||
get: getAction,
|
||||
invoke: invokeAction,
|
||||
list: listActions
|
||||
}
|
||||
})();
|
327
editor/js/ui/clipboard.js
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.clipboard = (function() {
|
||||
|
||||
var dialog;
|
||||
var dialogContainer;
|
||||
var exportNodesDialog;
|
||||
var importNodesDialog;
|
||||
var disabled = false;
|
||||
|
||||
function setupDialogs() {
|
||||
dialog = $('<div id="clipboard-dialog" class="hide node-red-dialog"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
id: "clipboard-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-close",
|
||||
class: "primary",
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-copy",
|
||||
class: "primary",
|
||||
text: RED._("clipboard.export.copy"),
|
||||
click: function() {
|
||||
$("#clipboard-export").select();
|
||||
document.execCommand("copy");
|
||||
document.getSelection().removeAllRanges();
|
||||
RED.notify(RED._("clipboard.nodesExported"));
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-ok",
|
||||
class: "primary",
|
||||
text: RED._("common.label.import"),
|
||||
click: function() {
|
||||
RED.view.importNodes($("#clipboard-import").val(),$("#import-tab > a.selected").attr('id') === 'import-tab-new');
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
},
|
||||
close: function(e) {
|
||||
}
|
||||
});
|
||||
|
||||
dialogContainer = dialog.children(".dialog-form");
|
||||
|
||||
exportNodesDialog =
|
||||
'<div class="form-row">'+
|
||||
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.export.copy"></label>'+
|
||||
'<span id="export-range-group" class="button-group">'+
|
||||
'<a id="export-range-selected" class="editor-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
|
||||
'<a id="export-range-flow" class="editor-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
|
||||
'<a id="export-range-full" class="editor-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
|
||||
'</span>'+
|
||||
'</div>'+
|
||||
'<div class="form-row">'+
|
||||
'<textarea readonly style="resize: none; width: 100%; border-radius: 4px;font-family: monospace; font-size: 12px; background:#f3f3f3; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
|
||||
'</div>'+
|
||||
'<div class="form-row" style="text-align: right;">'+
|
||||
'<span id="export-format-group" class="button-group">'+
|
||||
'<a id="export-format-mini" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
|
||||
'<a id="export-format-full" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
|
||||
'</span>'+
|
||||
'</div>';
|
||||
|
||||
importNodesDialog = '<div class="form-row">'+
|
||||
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+
|
||||
RED._("clipboard.pasteNodes")+
|
||||
'"></textarea>'+
|
||||
'</div>'+
|
||||
'<div class="form-row">'+
|
||||
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.import.import"></label>'+
|
||||
'<span id="import-tab" class="button-group">'+
|
||||
'<a id="import-tab-current" class="editor-button toggle selected" href="#" data-i18n="clipboard.export.current"></a>'+
|
||||
'<a id="import-tab-new" class="editor-button toggle" href="#" data-i18n="clipboard.import.newFlow"></a>'+
|
||||
'</span>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function validateImport() {
|
||||
var importInput = $("#clipboard-import");
|
||||
var v = importInput.val();
|
||||
v = v.substring(v.indexOf('['),v.lastIndexOf(']')+1);
|
||||
try {
|
||||
JSON.parse(v);
|
||||
importInput.removeClass("input-error");
|
||||
importInput.val(v);
|
||||
$("#clipboard-dialog-ok").button("enable");
|
||||
} catch(err) {
|
||||
if (v !== "") {
|
||||
importInput.addClass("input-error");
|
||||
}
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
}
|
||||
}
|
||||
|
||||
function importNodes() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(importNodesDialog));
|
||||
dialogContainer.i18n();
|
||||
|
||||
$("#clipboard-dialog-ok").show();
|
||||
$("#clipboard-dialog-cancel").show();
|
||||
$("#clipboard-dialog-close").hide();
|
||||
$("#clipboard-dialog-copy").hide();
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
$("#clipboard-import").keyup(validateImport);
|
||||
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
|
||||
|
||||
$("#import-tab > a").click(function(evt) {
|
||||
evt.preventDefault();
|
||||
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
|
||||
return;
|
||||
}
|
||||
$(this).parent().children().removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
});
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
|
||||
}
|
||||
|
||||
function exportNodes() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(exportNodesDialog));
|
||||
dialogContainer.i18n();
|
||||
|
||||
$("#export-format-group > a").click(function(evt) {
|
||||
evt.preventDefault();
|
||||
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
|
||||
return;
|
||||
}
|
||||
$(this).parent().children().removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
|
||||
var flow = $("#clipboard-export").val();
|
||||
if (flow.length > 0) {
|
||||
var nodes = JSON.parse(flow);
|
||||
|
||||
var format = $(this).attr('id');
|
||||
if (format === 'export-format-full') {
|
||||
flow = JSON.stringify(nodes,null,4);
|
||||
} else {
|
||||
flow = JSON.stringify(nodes);
|
||||
}
|
||||
$("#clipboard-export").val(flow);
|
||||
}
|
||||
});
|
||||
$("#export-range-group > a").click(function(evt) {
|
||||
evt.preventDefault();
|
||||
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
|
||||
return;
|
||||
}
|
||||
$(this).parent().children().removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
var type = $(this).attr('id');
|
||||
var flow = "";
|
||||
var nodes = null;
|
||||
if (type === 'export-range-selected') {
|
||||
var selection = RED.view.selection();
|
||||
nodes = RED.nodes.createExportableNodeSet(selection.nodes);
|
||||
} else if (type === 'export-range-flow') {
|
||||
var activeWorkspace = RED.workspaces.active();
|
||||
nodes = RED.nodes.filterNodes({z:activeWorkspace});
|
||||
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
|
||||
nodes.unshift(parentNode);
|
||||
nodes = RED.nodes.createExportableNodeSet(nodes);
|
||||
} else if (type === 'export-range-full') {
|
||||
nodes = RED.nodes.createCompleteNodeSet(false);
|
||||
}
|
||||
if (nodes !== null) {
|
||||
if (RED.settings.flowFilePretty) {
|
||||
flow = JSON.stringify(nodes,null,4);
|
||||
} else {
|
||||
flow = JSON.stringify(nodes);
|
||||
}
|
||||
}
|
||||
if (flow.length > 0) {
|
||||
$("#export-copy").removeClass('disabled');
|
||||
} else {
|
||||
$("#export-copy").addClass('disabled');
|
||||
}
|
||||
$("#clipboard-export").val(flow);
|
||||
})
|
||||
|
||||
$("#clipboard-dialog-ok").hide();
|
||||
$("#clipboard-dialog-cancel").hide();
|
||||
$("#clipboard-dialog-copy").hide();
|
||||
$("#clipboard-dialog-close").hide();
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
$("#export-range-selected").click();
|
||||
} else {
|
||||
$("#export-range-selected").addClass('disabled').removeClass('selected');
|
||||
$("#export-range-flow").click();
|
||||
}
|
||||
if (RED.settings.flowFilePretty) {
|
||||
$("#export-format-full").click();
|
||||
} else {
|
||||
$("#export-format-mini").click();
|
||||
}
|
||||
$("#clipboard-export")
|
||||
.focus(function() {
|
||||
var textarea = $(this);
|
||||
textarea.select();
|
||||
textarea.mouseup(function() {
|
||||
textarea.unbind("mouseup");
|
||||
return false;
|
||||
})
|
||||
});
|
||||
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
|
||||
|
||||
setTimeout(function() {
|
||||
$("#clipboard-export").focus();
|
||||
if (!document.queryCommandEnabled("copy")) {
|
||||
$("#clipboard-dialog-cancel").hide();
|
||||
$("#clipboard-dialog-close").show();
|
||||
} else {
|
||||
$("#clipboard-dialog-cancel").show();
|
||||
$("#clipboard-dialog-copy").show();
|
||||
}
|
||||
|
||||
},0);
|
||||
}
|
||||
|
||||
function hideDropTarget() {
|
||||
$("#dropTarget").hide();
|
||||
RED.keyboard.remove("escape");
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
setupDialogs();
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-export",true);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",true);
|
||||
RED.menu.setDisabled("menu-item-export-library",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-export",false);
|
||||
RED.menu.setDisabled("menu-item-export-clipboard",false);
|
||||
RED.menu.setDisabled("menu-item-export-library",false);
|
||||
}
|
||||
});
|
||||
|
||||
RED.actions.add("core:show-export-dialog",exportNodes);
|
||||
RED.actions.add("core:show-import-dialog",importNodes);
|
||||
|
||||
|
||||
RED.events.on("editor:open",function() { disabled = true; });
|
||||
RED.events.on("editor:close",function() { disabled = false; });
|
||||
RED.events.on("search:open",function() { disabled = true; });
|
||||
RED.events.on("search:close",function() { disabled = false; });
|
||||
RED.events.on("type-search:open",function() { disabled = true; });
|
||||
RED.events.on("type-search:close",function() { disabled = false; });
|
||||
RED.events.on("palette-editor:open",function() { disabled = true; });
|
||||
RED.events.on("palette-editor:close",function() { disabled = false; });
|
||||
|
||||
|
||||
$('#chart').on("dragenter",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
$("#dropTarget").css({display:'table'});
|
||||
RED.keyboard.add("*", "escape" ,hideDropTarget);
|
||||
}
|
||||
});
|
||||
|
||||
$('#dropTarget').on("dragover",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
.on("dragleave",function(event) {
|
||||
hideDropTarget();
|
||||
})
|
||||
.on("drop",function(event) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
hideDropTarget();
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
RED.view.importNodes(data);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
},
|
||||
import: importNodes,
|
||||
export: exportNodes
|
||||
}
|
||||
})();
|
307
editor/js/ui/common/editableList.js
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* options:
|
||||
* - addButton : boolean|string - text for add label, default 'add'
|
||||
* - height : number|'auto'
|
||||
* - resize : function - called when list as a whole is resized
|
||||
* - resizeItem : function(item) - called to resize individual item
|
||||
* - sortable : boolean|string - string is the css selector for handle
|
||||
* - sortItems : function(items) - when order of items changes
|
||||
* - connectWith : css selector of other sortables
|
||||
* - removable : boolean - whether to display delete button on items
|
||||
* - addItem : function(row,index,itemData) - when an item is added
|
||||
* - removeItem : function(itemData) - called when an item is removed
|
||||
* - filter : function(itemData) - called for each item to determine if it should be shown
|
||||
* - sort : function(itemDataA,itemDataB) - called to sort items
|
||||
* - scrollOnAdd : boolean - whether to scroll to newly added items
|
||||
* methods:
|
||||
* - addItem(itemData)
|
||||
* - removeItem(itemData)
|
||||
* - width(width)
|
||||
* - height(height)
|
||||
* - items()
|
||||
* - empty()
|
||||
* - filter(filter)
|
||||
* - sort(sort)
|
||||
* - length()
|
||||
*/
|
||||
$.widget( "nodered.editableList", {
|
||||
_create: function() {
|
||||
var that = this;
|
||||
|
||||
this.element.addClass('red-ui-editableList-list');
|
||||
this.uiWidth = this.element.width();
|
||||
this.uiContainer = this.element
|
||||
.wrap( "<div>" )
|
||||
.parent();
|
||||
this.topContainer = this.uiContainer.wrap("<div>").parent();
|
||||
|
||||
this.topContainer.addClass('red-ui-editableList');
|
||||
|
||||
if (this.options.addButton !== false) {
|
||||
var addLabel;
|
||||
if (typeof this.options.addButton === 'string') {
|
||||
addLabel = this.options.addButton
|
||||
} else {
|
||||
if (RED && RED._) {
|
||||
addLabel = RED._("editableList.add");
|
||||
} else {
|
||||
addLabel = 'add';
|
||||
}
|
||||
}
|
||||
$('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
|
||||
.appendTo(this.topContainer)
|
||||
.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
that.addItem({});
|
||||
});
|
||||
}
|
||||
if (this.element.css("position") === "absolute") {
|
||||
["top","left","bottom","right"].forEach(function(s) {
|
||||
var v = that.element.css(s);
|
||||
if (s!=="auto" && s!=="") {
|
||||
that.topContainer.css(s,v);
|
||||
that.uiContainer.css(s,"0");
|
||||
that.element.css(s,'auto');
|
||||
}
|
||||
})
|
||||
this.element.css("position","static");
|
||||
this.topContainer.css("position","absolute");
|
||||
this.uiContainer.css("position","absolute");
|
||||
|
||||
}
|
||||
this.uiContainer.addClass("red-ui-editableList-container");
|
||||
|
||||
this.uiHeight = this.element.height();
|
||||
|
||||
this.activeFilter = this.options.filter||null;
|
||||
this.activeSort = this.options.sort||null;
|
||||
this.scrollOnAdd = this.options.scrollOnAdd;
|
||||
if (this.scrollOnAdd === undefined) {
|
||||
this.scrollOnAdd = true;
|
||||
}
|
||||
var minHeight = this.element.css("minHeight");
|
||||
if (minHeight !== '0px') {
|
||||
this.uiContainer.css("minHeight",minHeight);
|
||||
this.element.css("minHeight",0);
|
||||
}
|
||||
if (this.options.height !== 'auto') {
|
||||
this.uiContainer.css("overflow-y","scroll");
|
||||
if (!isNaN(this.options.height)) {
|
||||
this.uiHeight = this.options.height;
|
||||
}
|
||||
}
|
||||
this.element.height('auto');
|
||||
|
||||
var attrStyle = this.element.attr('style');
|
||||
var m;
|
||||
if ((m = /width\s*:\s*(\d+%)/i.exec(attrStyle)) !== null) {
|
||||
this.element.width('100%');
|
||||
this.uiContainer.width(m[1]);
|
||||
}
|
||||
if (this.options.sortable) {
|
||||
var handle = (typeof this.options.sortable === 'string')?
|
||||
this.options.sortable :
|
||||
".red-ui-editableList-item-handle";
|
||||
var sortOptions = {
|
||||
axis: "y",
|
||||
update: function( event, ui ) {
|
||||
if (that.options.sortItems) {
|
||||
that.options.sortItems(that.items());
|
||||
}
|
||||
},
|
||||
handle:handle,
|
||||
cursor: "move",
|
||||
tolerance: "pointer",
|
||||
forcePlaceholderSize:true,
|
||||
placeholder: "red-ui-editabelList-item-placeholder",
|
||||
start: function(e, ui){
|
||||
ui.placeholder.height(ui.item.height()-4);
|
||||
}
|
||||
};
|
||||
if (this.options.connectWith) {
|
||||
sortOptions.connectWith = this.options.connectWith;
|
||||
}
|
||||
|
||||
this.element.sortable(sortOptions);
|
||||
}
|
||||
|
||||
this._resize();
|
||||
|
||||
// this.menu = this._createMenu(this.types, function(v) { that.type(v) });
|
||||
// this.type(this.options.default||this.types[0].value);
|
||||
},
|
||||
_resize: function() {
|
||||
var currentFullHeight = this.topContainer.height();
|
||||
var innerHeight = this.uiContainer.height();
|
||||
var delta = currentFullHeight - innerHeight;
|
||||
if (this.uiHeight !== 0) {
|
||||
this.uiContainer.height(this.uiHeight-delta);
|
||||
}
|
||||
if (this.options.resize) {
|
||||
this.options.resize();
|
||||
}
|
||||
if (this.options.resizeItem) {
|
||||
var that = this;
|
||||
this.element.children().each(function(i) {
|
||||
that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i);
|
||||
});
|
||||
}
|
||||
},
|
||||
_destroy: function() {
|
||||
},
|
||||
_refreshFilter: function() {
|
||||
var that = this;
|
||||
var count = 0;
|
||||
if (!this.activeFilter) {
|
||||
this.element.children().show();
|
||||
}
|
||||
var items = this.items();
|
||||
items.each(function (i,el) {
|
||||
var data = el.data('data');
|
||||
try {
|
||||
if (that.activeFilter(data)) {
|
||||
el.parent().show();
|
||||
count++;
|
||||
} else {
|
||||
el.parent().hide();
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
el.parent().show();
|
||||
count++;
|
||||
}
|
||||
});
|
||||
return count;
|
||||
},
|
||||
_refreshSort: function() {
|
||||
if (this.activeSort) {
|
||||
var items = this.element.children();
|
||||
var that = this;
|
||||
items.sort(function(A,B) {
|
||||
return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data'));
|
||||
});
|
||||
$.each(items,function(idx,li) {
|
||||
that.element.append(li);
|
||||
})
|
||||
}
|
||||
},
|
||||
width: function(desiredWidth) {
|
||||
this.uiWidth = desiredWidth;
|
||||
this._resize();
|
||||
},
|
||||
height: function(desiredHeight) {
|
||||
this.uiHeight = desiredHeight;
|
||||
this._resize();
|
||||
},
|
||||
addItem: function(data) {
|
||||
var that = this;
|
||||
data = data || {};
|
||||
var li = $('<li>');
|
||||
var added = false;
|
||||
if (this.activeSort) {
|
||||
var items = this.items();
|
||||
var skip = false;
|
||||
items.each(function(i,el) {
|
||||
if (added) { return }
|
||||
var itemData = el.data('data');
|
||||
if (that.activeSort(data,itemData) < 0) {
|
||||
li.insertBefore(el.closest("li"));
|
||||
added = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!added) {
|
||||
li.appendTo(this.element);
|
||||
}
|
||||
var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
|
||||
row.data('data',data);
|
||||
if (this.options.sortable === true) {
|
||||
$('<i class="red-ui-editableList-item-handle fa fa-bars"></i>').appendTo(li);
|
||||
li.addClass("red-ui-editableList-item-sortable");
|
||||
}
|
||||
if (this.options.removable) {
|
||||
var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(li);
|
||||
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
|
||||
li.addClass("red-ui-editableList-item-removable");
|
||||
deleteButton.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
var data = row.data('data');
|
||||
li.addClass("red-ui-editableList-item-deleting")
|
||||
li.fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
if (that.options.removeItem) {
|
||||
that.options.removeItem(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (this.options.addItem) {
|
||||
var index = that.element.children().length-1;
|
||||
setTimeout(function() {
|
||||
that.options.addItem(row,index,data);
|
||||
if (that.activeFilter) {
|
||||
try {
|
||||
if (!that.activeFilter(data)) {
|
||||
li.hide();
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!that.activeSort && that.scrollOnAdd) {
|
||||
setTimeout(function() {
|
||||
that.uiContainer.scrollTop(that.element.height());
|
||||
},0);
|
||||
}
|
||||
},0);
|
||||
}
|
||||
},
|
||||
removeItem: function(data) {
|
||||
var items = this.element.children().filter(function(f) {
|
||||
return data === $(this).find(".red-ui-editableList-item-content").data('data');
|
||||
});
|
||||
items.remove();
|
||||
if (this.options.removeItem) {
|
||||
this.options.removeItem(data);
|
||||
}
|
||||
},
|
||||
items: function() {
|
||||
return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); });
|
||||
},
|
||||
empty: function() {
|
||||
this.element.empty();
|
||||
},
|
||||
filter: function(filter) {
|
||||
if (filter !== undefined) {
|
||||
this.activeFilter = filter;
|
||||
}
|
||||
return this._refreshFilter();
|
||||
},
|
||||
sort: function(sort) {
|
||||
if (sort !== undefined) {
|
||||
this.activeSort = sort;
|
||||
}
|
||||
return this._refreshSort();
|
||||
},
|
||||
length: function() {
|
||||
return this.element.children().length;
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,9 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
|
||||
RED.menu = (function() {
|
||||
|
||||
var menuItems = {};
|
||||
@@ -23,21 +20,28 @@ RED.menu = (function() {
|
||||
function createMenuItem(opt) {
|
||||
var item;
|
||||
|
||||
function setState() {
|
||||
if (opt !== null && opt.id) {
|
||||
var themeSetting = RED.settings.theme("menu."+opt.id);
|
||||
if (themeSetting === false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setInitialState() {
|
||||
var savedStateActive = isSavedStateActive(opt.id);
|
||||
if (savedStateActive) {
|
||||
link.addClass("active");
|
||||
opt.onselect.call(opt, true);
|
||||
triggerAction(opt.id,true);
|
||||
} else if (savedStateActive === false) {
|
||||
link.removeClass("active");
|
||||
opt.onselect.call(opt, false);
|
||||
triggerAction(opt.id,false);
|
||||
} else if (opt.hasOwnProperty("selected")) {
|
||||
if (opt.selected) {
|
||||
link.addClass("active");
|
||||
} else {
|
||||
link.removeClass("active");
|
||||
}
|
||||
opt.onselect.call(opt, opt.selected);
|
||||
triggerAction(opt.id,opt.selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +49,16 @@ RED.menu = (function() {
|
||||
item = $('<li class="divider"></li>');
|
||||
} else {
|
||||
item = $('<li></li>');
|
||||
|
||||
|
||||
if (opt.group) {
|
||||
item.addClass("menu-group-"+opt.group);
|
||||
|
||||
}
|
||||
var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
|
||||
if (opt.toggle) {
|
||||
linkContent += '<i class="fa fa-square pull-left"></i>';
|
||||
linkContent += '<i class="fa fa-check-square pull-left"></i>';
|
||||
|
||||
|
||||
}
|
||||
if (opt.icon !== undefined) {
|
||||
if (/\.png/.test(opt.icon)) {
|
||||
@@ -59,16 +67,16 @@ RED.menu = (function() {
|
||||
linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (opt.sublabel) {
|
||||
linkContent += '<span class="menu-label-container"><span class="menu-label">'+opt.label+'</span>'+
|
||||
'<span class="menu-sublabel">'+opt.sublabel+'</span></span>'
|
||||
} else {
|
||||
linkContent += '<span class="menu-label">'+opt.label+'</span>'
|
||||
}
|
||||
|
||||
|
||||
linkContent += '</a>';
|
||||
|
||||
|
||||
var link = $(linkContent).appendTo(item);
|
||||
|
||||
menuItems[opt.id] = opt;
|
||||
@@ -96,10 +104,12 @@ RED.menu = (function() {
|
||||
setSelected(opt.id, !selected);
|
||||
}
|
||||
} else {
|
||||
opt.onselect.call(opt);
|
||||
triggerAction(opt.id);
|
||||
}
|
||||
});
|
||||
setState();
|
||||
if (opt.toggle) {
|
||||
setInitialState();
|
||||
}
|
||||
} else if (opt.href) {
|
||||
link.attr("target","_blank").attr("href",opt.href);
|
||||
} else if (!opt.options) {
|
||||
@@ -113,23 +123,15 @@ RED.menu = (function() {
|
||||
var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item);
|
||||
|
||||
for (var i=0;i<opt.options.length;i++) {
|
||||
createMenuItem(opt.options[i]).appendTo(submenu);
|
||||
var li = createMenuItem(opt.options[i]);
|
||||
if (li) {
|
||||
li.appendTo(submenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt.disabled) {
|
||||
item.addClass("disabled");
|
||||
}
|
||||
if (opt.tip) {
|
||||
item.popover({
|
||||
placement:"left",
|
||||
trigger: "hover",
|
||||
delay: { show: 350, hide: 20 },
|
||||
html: true,
|
||||
container:'body',
|
||||
content: opt.tip
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -144,13 +146,33 @@ RED.menu = (function() {
|
||||
// $("#"+options.id+"-submenu").show();
|
||||
// event.preventDefault();
|
||||
//});
|
||||
|
||||
|
||||
|
||||
|
||||
var topMenu = $("<ul/>",{id:options.id+"-submenu", class:"dropdown-menu pull-right"}).insertAfter(button);
|
||||
|
||||
var lastAddedSeparator = false;
|
||||
for (var i=0;i<options.options.length;i++) {
|
||||
var opt = options.options[i];
|
||||
createMenuItem(opt).appendTo(topMenu);
|
||||
if (opt !== null || !lastAddedSeparator) {
|
||||
var li = createMenuItem(opt);
|
||||
if (li) {
|
||||
li.appendTo(topMenu);
|
||||
lastAddedSeparator = (opt === null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function triggerAction(id, args) {
|
||||
var opt = menuItems[id];
|
||||
var callback = opt.onselect;
|
||||
if (typeof opt.onselect === 'string') {
|
||||
callback = RED.actions.get(opt.onselect);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(opt,args);
|
||||
} else {
|
||||
console.log("No callback for",id,opt.onselect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,12 +198,16 @@ RED.menu = (function() {
|
||||
} else {
|
||||
$("#"+id).removeClass("active");
|
||||
}
|
||||
if (opt.onselect) {
|
||||
opt.onselect.call(opt,state);
|
||||
if (opt && opt.onselect) {
|
||||
triggerAction(opt.id,state);
|
||||
}
|
||||
setSavedState(id, state);
|
||||
}
|
||||
|
||||
function toggleSelected(id) {
|
||||
setSelected(id,!isSelected(id));
|
||||
}
|
||||
|
||||
function setDisabled(id,state) {
|
||||
if (state) {
|
||||
$("#"+id).parent().addClass("disabled");
|
||||
@@ -191,30 +217,44 @@ RED.menu = (function() {
|
||||
}
|
||||
|
||||
function addItem(id,opt) {
|
||||
createMenuItem(opt).appendTo("#"+id+"-submenu");
|
||||
var item = createMenuItem(opt);
|
||||
if (opt.group) {
|
||||
var groupItems = $("#"+id+"-submenu").children(".menu-group-"+opt.group);
|
||||
if (groupItems.length === 0) {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
} else {
|
||||
for (var i=0;i<groupItems.length;i++) {
|
||||
var groupItem = groupItems[i];
|
||||
var label = $(groupItem).find(".menu-label").html();
|
||||
if (opt.label < label) {
|
||||
$(groupItem).before(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i === groupItems.length) {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item.appendTo("#"+id+"-submenu");
|
||||
}
|
||||
}
|
||||
function removeItem(id) {
|
||||
$("#"+id).parent().remove();
|
||||
}
|
||||
|
||||
function setAction(id,action) {
|
||||
menuItems[id].onselect = action;
|
||||
$("#"+id).click(function() {
|
||||
if ($(this).parent().hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
if (menuItems[id].toggle) {
|
||||
setSelected(id,!isSelected(id));
|
||||
} else {
|
||||
menuItems[id].onselect.call(menuItems[id]);
|
||||
}
|
||||
});
|
||||
var opt = menuItems[id];
|
||||
if (opt) {
|
||||
opt.onselect = action;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: createMenu,
|
||||
setSelected: setSelected,
|
||||
isSelected: isSelected,
|
||||
toggleSelected: toggleSelected,
|
||||
setDisabled: setDisabled,
|
||||
addItem: addItem,
|
||||
removeItem: removeItem,
|
79
editor/js/ui/common/popover.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.popover = (function() {
|
||||
|
||||
|
||||
function createPopover(options) {
|
||||
var target = options.target;
|
||||
|
||||
var content = options.content;
|
||||
var delay = options.delay;
|
||||
var timer = null;
|
||||
var active;
|
||||
var div;
|
||||
|
||||
var openPopup = function() {
|
||||
if (active) {
|
||||
div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body");
|
||||
var targetPos = target.offset();
|
||||
var targetWidth = target.width();
|
||||
var targetHeight = target.height();
|
||||
|
||||
var divHeight = div.height();
|
||||
div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:targetPos.left+targetWidth+17});
|
||||
|
||||
div.fadeIn("fast");
|
||||
}
|
||||
}
|
||||
var closePopup = function() {
|
||||
if (!active) {
|
||||
if (div) {
|
||||
div.fadeOut("fast",function() {
|
||||
$(this).remove();
|
||||
});
|
||||
div = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.on('mouseenter',function(e) {
|
||||
clearTimeout(timer);
|
||||
active = true;
|
||||
timer = setTimeout(openPopup,delay.show);
|
||||
});
|
||||
target.on('mouseleave', function(e) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
active = false;
|
||||
setTimeout(closePopup,delay.hide);
|
||||
});
|
||||
var res = {
|
||||
setContent: function(_content) {
|
||||
content = _content;
|
||||
}
|
||||
}
|
||||
target.data('popover',res);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
create: createPopover
|
||||
}
|
||||
|
||||
})();
|
96
editor/js/ui/common/searchBox.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
|
||||
$.widget( "nodered.searchBox", {
|
||||
_create: function() {
|
||||
var that = this;
|
||||
|
||||
this.currentTimeout = null;
|
||||
this.lastSent = "";
|
||||
this.element.val("");
|
||||
this.uiContainer = this.element.wrap("<div>").parent();
|
||||
this.uiContainer.addClass("red-ui-searchBox-container");
|
||||
|
||||
$('<i class="fa fa-search"></i>').prependTo(this.uiContainer);
|
||||
this.clearButton = $('<a href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer);
|
||||
this.clearButton.on("click",function(e) {
|
||||
e.preventDefault();
|
||||
that.element.val("");
|
||||
that._change("",true);
|
||||
that.element.focus();
|
||||
});
|
||||
|
||||
this.resultCount = $('<span>',{class:"red-ui-searchBox-resultCount hide"}).appendTo(this.uiContainer);
|
||||
|
||||
this.element.val("");
|
||||
this.element.on("keydown",function(evt) {
|
||||
if (evt.keyCode === 27) {
|
||||
that.element.val("");
|
||||
}
|
||||
})
|
||||
this.element.on("keyup",function(evt) {
|
||||
that._change($(this).val());
|
||||
});
|
||||
|
||||
this.element.on("focus",function() {
|
||||
$("body").one("mousedown",function() {
|
||||
that.element.blur();
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
_change: function(val,instant) {
|
||||
var fireEvent = false;
|
||||
if (val === "") {
|
||||
this.clearButton.hide();
|
||||
fireEvent = true;
|
||||
} else {
|
||||
this.clearButton.show();
|
||||
fireEvent = (val.length >= (this.options.minimumLength||0));
|
||||
}
|
||||
var current = this.element.val();
|
||||
fireEvent = fireEvent && current !== this.lastSent;
|
||||
if (fireEvent) {
|
||||
if (!instant && this.options.delay > 0) {
|
||||
clearTimeout(this.currentTimeout);
|
||||
var that = this;
|
||||
this.currentTimeout = setTimeout(function() {
|
||||
that.lastSent = that.element.val();
|
||||
that._trigger("change");
|
||||
},this.options.delay);
|
||||
} else {
|
||||
this._trigger("change");
|
||||
}
|
||||
}
|
||||
},
|
||||
value: function(val) {
|
||||
if (val === undefined) {
|
||||
return this.element.val();
|
||||
} else {
|
||||
this.element.val(val);
|
||||
this._change(val);
|
||||
}
|
||||
},
|
||||
count: function(val) {
|
||||
if (val === undefined || val === null || val === "") {
|
||||
this.resultCount.text("").hide();
|
||||
} else {
|
||||
this.resultCount.text(val).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
368
editor/js/ui/common/tabs.js
Normal file
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
|
||||
RED.tabs = (function() {
|
||||
function createTabs(options) {
|
||||
var tabs = {};
|
||||
var currentTabWidth;
|
||||
var currentActiveTabWidth = 0;
|
||||
|
||||
var ul = $("#"+options.id);
|
||||
var wrapper = ul.wrap( "<div>" ).parent();
|
||||
var scrollContainer = ul.wrap( "<div>" ).parent();
|
||||
wrapper.addClass("red-ui-tabs");
|
||||
if (options.addButton && typeof options.addButton === 'function') {
|
||||
wrapper.addClass("red-ui-tabs-add");
|
||||
var addButton = $('<div class="red-ui-tab-button"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper);
|
||||
addButton.find('a').click(function(evt) {
|
||||
evt.preventDefault();
|
||||
options.addButton();
|
||||
})
|
||||
|
||||
}
|
||||
var scrollLeft;
|
||||
var scrollRight;
|
||||
|
||||
if (options.scrollable) {
|
||||
wrapper.addClass("red-ui-tabs-scrollable");
|
||||
scrollContainer.addClass("red-ui-tabs-scroll-container");
|
||||
scrollContainer.scroll(updateScroll);
|
||||
scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a");
|
||||
scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();});
|
||||
scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a");
|
||||
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();});
|
||||
}
|
||||
function scrollEventHandler(evt,dir) {
|
||||
evt.preventDefault();
|
||||
if ($(this).hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
var currentScrollLeft = scrollContainer.scrollLeft();
|
||||
scrollContainer.animate( { scrollLeft: dir }, 100);
|
||||
var interval = setInterval(function() {
|
||||
var newScrollLeft = scrollContainer.scrollLeft()
|
||||
if (newScrollLeft === currentScrollLeft) {
|
||||
clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
currentScrollLeft = newScrollLeft;
|
||||
scrollContainer.animate( { scrollLeft: dir }, 100);
|
||||
},100);
|
||||
$(this).one('mouseup',function() {
|
||||
clearInterval(interval);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
ul.children().first().addClass("active");
|
||||
ul.children().addClass("red-ui-tab");
|
||||
|
||||
function onTabClick() {
|
||||
activateTab($(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateScroll() {
|
||||
if (ul.children().length !== 0) {
|
||||
var sl = scrollContainer.scrollLeft();
|
||||
var scWidth = scrollContainer.width();
|
||||
var ulWidth = ul.width();
|
||||
if (sl === 0) {
|
||||
scrollLeft.hide();
|
||||
} else {
|
||||
scrollLeft.show();
|
||||
}
|
||||
if (sl === ulWidth-scWidth) {
|
||||
scrollRight.hide();
|
||||
} else {
|
||||
scrollRight.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
function onTabDblClick() {
|
||||
if (options.ondblclick) {
|
||||
options.ondblclick(tabs[$(this).attr('href').slice(1)]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function activateTab(link) {
|
||||
if (typeof link === "string") {
|
||||
link = ul.find("a[href='#"+link+"']");
|
||||
}
|
||||
if (link.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (!link.parent().hasClass("active")) {
|
||||
ul.children().removeClass("active");
|
||||
ul.children().css({"transition": "width 100ms"});
|
||||
link.parent().addClass("active");
|
||||
if (options.scrollable) {
|
||||
var pos = link.parent().position().left;
|
||||
if (pos-21 < 0) {
|
||||
scrollContainer.animate( { scrollLeft: '+='+(pos-50) }, 300);
|
||||
} else if (pos + 120 > scrollContainer.width()) {
|
||||
scrollContainer.animate( { scrollLeft: '+='+(pos + 140-scrollContainer.width()) }, 300);
|
||||
}
|
||||
}
|
||||
if (options.onchange) {
|
||||
options.onchange(tabs[link.attr('href').slice(1)]);
|
||||
}
|
||||
updateTabWidths();
|
||||
setTimeout(function() {
|
||||
ul.children().css({"transition": ""});
|
||||
},100);
|
||||
}
|
||||
}
|
||||
function activatePreviousTab() {
|
||||
var previous = ul.find("li.active").prev();
|
||||
if (previous.length > 0) {
|
||||
activateTab(previous.find("a"));
|
||||
}
|
||||
}
|
||||
function activateNextTab() {
|
||||
var next = ul.find("li.active").next();
|
||||
if (next.length > 0) {
|
||||
activateTab(next.find("a"));
|
||||
}
|
||||
}
|
||||
|
||||
function updateTabWidths() {
|
||||
var tabs = ul.find("li.red-ui-tab");
|
||||
var width = wrapper.width();
|
||||
var tabCount = tabs.size();
|
||||
var tabWidth = (width-12-(tabCount*6))/tabCount;
|
||||
currentTabWidth = (100*tabWidth/width)+"%";
|
||||
currentActiveTabWidth = currentTabWidth+"%";
|
||||
if (options.scrollable) {
|
||||
tabWidth = Math.max(tabWidth,140);
|
||||
currentTabWidth = tabWidth+"px";
|
||||
currentActiveTabWidth = 0;
|
||||
var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount);
|
||||
ul.width(listWidth);
|
||||
updateScroll();
|
||||
} else if (options.hasOwnProperty("minimumActiveTabWidth")) {
|
||||
if (tabWidth < options.minimumActiveTabWidth) {
|
||||
tabCount -= 1;
|
||||
tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount;
|
||||
currentTabWidth = (100*tabWidth/width)+"%";
|
||||
currentActiveTabWidth = options.minimumActiveTabWidth+"px";
|
||||
} else {
|
||||
currentActiveTabWidth = 0;
|
||||
}
|
||||
}
|
||||
tabs.css({width:currentTabWidth});
|
||||
if (tabWidth < 50) {
|
||||
ul.find(".red-ui-tab-close").hide();
|
||||
ul.find(".red-ui-tab-icon").hide();
|
||||
ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"})
|
||||
} else {
|
||||
ul.find(".red-ui-tab-close").show();
|
||||
ul.find(".red-ui-tab-icon").show();
|
||||
ul.find(".red-ui-tab-label").css({paddingLeft:""})
|
||||
}
|
||||
if (currentActiveTabWidth !== 0) {
|
||||
ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick);
|
||||
setTimeout(function() {
|
||||
updateTabWidths();
|
||||
},0);
|
||||
|
||||
|
||||
function removeTab(id) {
|
||||
var li = ul.find("a[href='#"+id+"']").parent();
|
||||
if (li.hasClass("active")) {
|
||||
var tab = li.prev();
|
||||
if (tab.size() === 0) {
|
||||
tab = li.next();
|
||||
}
|
||||
activateTab(tab.find("a"));
|
||||
}
|
||||
li.remove();
|
||||
if (options.onremove) {
|
||||
options.onremove(tabs[id]);
|
||||
}
|
||||
delete tabs[id];
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
return {
|
||||
addTab: function(tab) {
|
||||
tabs[tab.id] = tab;
|
||||
var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
|
||||
li.data("tabId",tab.id);
|
||||
var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
|
||||
if (tab.icon) {
|
||||
$('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
|
||||
}
|
||||
var span = $('<span/>',{class:"bidiAware"}).text(tab.label).appendTo(link);
|
||||
span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label));
|
||||
|
||||
link.on("click",onTabClick);
|
||||
link.on("dblclick",onTabDblClick);
|
||||
if (tab.closeable) {
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
closeLink.append('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.on("click",function(event) {
|
||||
removeTab(tab.id);
|
||||
});
|
||||
}
|
||||
updateTabWidths();
|
||||
if (options.onadd) {
|
||||
options.onadd(tab);
|
||||
}
|
||||
link.attr("title",tab.label);
|
||||
if (ul.find("li.red-ui-tab").size() == 1) {
|
||||
activateTab(link);
|
||||
}
|
||||
if (options.onreorder) {
|
||||
var originalTabOrder;
|
||||
var tabDragIndex;
|
||||
var tabElements = [];
|
||||
var startDragIndex;
|
||||
|
||||
li.draggable({
|
||||
axis:"x",
|
||||
distance: 20,
|
||||
start: function(event,ui) {
|
||||
originalTabOrder = [];
|
||||
tabElements = [];
|
||||
ul.children().each(function(i) {
|
||||
tabElements[i] = {
|
||||
el:$(this),
|
||||
text: $(this).text(),
|
||||
left: $(this).position().left,
|
||||
width: $(this).width()
|
||||
};
|
||||
if ($(this).is(li)) {
|
||||
tabDragIndex = i;
|
||||
startDragIndex = i;
|
||||
}
|
||||
originalTabOrder.push($(this).data("tabId"));
|
||||
});
|
||||
ul.children().each(function(i) {
|
||||
if (i!==tabDragIndex) {
|
||||
$(this).css({
|
||||
position: 'absolute',
|
||||
left: tabElements[i].left+"px",
|
||||
width: tabElements[i].width+2,
|
||||
transition: "left 0.3s"
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
if (!li.hasClass('active')) {
|
||||
li.css({'zIndex':1});
|
||||
}
|
||||
},
|
||||
drag: function(event,ui) {
|
||||
ui.position.left += tabElements[tabDragIndex].left+scrollContainer.scrollLeft();
|
||||
var tabCenter = ui.position.left + tabElements[tabDragIndex].width/2 - scrollContainer.scrollLeft();
|
||||
for (var i=0;i<tabElements.length;i++) {
|
||||
if (i === tabDragIndex) {
|
||||
continue;
|
||||
}
|
||||
if (tabCenter > tabElements[i].left && tabCenter < tabElements[i].left+tabElements[i].width) {
|
||||
if (i < tabDragIndex) {
|
||||
tabElements[i].left += tabElements[tabDragIndex].width+8;
|
||||
tabElements[tabDragIndex].el.detach().insertBefore(tabElements[i].el);
|
||||
} else {
|
||||
tabElements[i].left -= tabElements[tabDragIndex].width+8;
|
||||
tabElements[tabDragIndex].el.detach().insertAfter(tabElements[i].el);
|
||||
}
|
||||
tabElements[i].el.css({left:tabElements[i].left+"px"});
|
||||
|
||||
tabElements.splice(i, 0, tabElements.splice(tabDragIndex, 1)[0]);
|
||||
|
||||
tabDragIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: function(event,ui) {
|
||||
ul.children().css({position:"relative",left:"",transition:""});
|
||||
if (!li.hasClass('active')) {
|
||||
li.css({zIndex:""});
|
||||
}
|
||||
updateTabWidths();
|
||||
if (startDragIndex !== tabDragIndex) {
|
||||
options.onreorder(originalTabOrder, $.makeArray(ul.children().map(function() { return $(this).data('tabId');})));
|
||||
}
|
||||
activateTab(tabElements[tabDragIndex].el.data('tabId'));
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
removeTab: removeTab,
|
||||
activateTab: activateTab,
|
||||
nextTab: activateNextTab,
|
||||
previousTab: activatePreviousTab,
|
||||
resize: updateTabWidths,
|
||||
count: function() {
|
||||
return ul.find("li.red-ui-tab").size();
|
||||
},
|
||||
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.find("span").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label));
|
||||
updateTabWidths();
|
||||
},
|
||||
order: function(order) {
|
||||
var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));
|
||||
if (existingTabOrder.length !== order.length) {
|
||||
return
|
||||
}
|
||||
var i;
|
||||
var match = true;
|
||||
for (i=0;i<order.length;i++) {
|
||||
if (order[i] !== existingTabOrder[i]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return;
|
||||
}
|
||||
var existingTabMap = {};
|
||||
var existingTabs = ul.children().detach().each(function() {
|
||||
existingTabMap[$(this).data("tabId")] = $(this);
|
||||
});
|
||||
for (i=0;i<order.length;i++) {
|
||||
existingTabMap[order[i]].appendTo(ul);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
create: createTabs
|
||||
}
|
||||
})();
|
434
editor/js/ui/common/typedInput.js
Normal file
@@ -0,0 +1,434 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
var allOptions = {
|
||||
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression},
|
||||
flow: {value:"flow",label:"flow.",validate:RED.utils.validatePropertyExpression},
|
||||
global: {value:"global",label:"global.",validate:RED.utils.validatePropertyExpression},
|
||||
str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"},
|
||||
num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
|
||||
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]},
|
||||
json: {value:"json",label:"JSON",icon:"red/images/typedInput/json.png", validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}},
|
||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.png"},
|
||||
date: {value:"date",label:"timestamp",hasValue:false},
|
||||
jsonata: {
|
||||
value: "jsonata",
|
||||
label: "expression",
|
||||
icon: "red/images/typedInput/expr.png",
|
||||
validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}},
|
||||
expand:function() {
|
||||
var that = this;
|
||||
RED.editor.editExpression({
|
||||
value: this.value().replace(/\t/g,"\n"),
|
||||
complete: function(v) {
|
||||
that.value(v.replace(/\n/g,"\t"));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
var nlsd = false;
|
||||
|
||||
$.widget( "nodered.typedInput", {
|
||||
_create: function() {
|
||||
if (!nlsd && RED && RED._) {
|
||||
for (var i in allOptions) {
|
||||
if (allOptions.hasOwnProperty(i)) {
|
||||
allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label});
|
||||
}
|
||||
}
|
||||
}
|
||||
nlsd = true;
|
||||
var that = this;
|
||||
|
||||
this.disarmClick = false;
|
||||
this.element.addClass('red-ui-typedInput');
|
||||
this.uiWidth = this.element.outerWidth();
|
||||
this.elementDiv = this.element.wrap("<div>").parent().addClass('red-ui-typedInput-input');
|
||||
this.uiSelect = this.elementDiv.wrap( "<div>" ).parent();
|
||||
var attrStyle = this.element.attr('style');
|
||||
var m;
|
||||
if ((m = /width\s*:\s*(\d+(%|px))/i.exec(attrStyle)) !== null) {
|
||||
this.element.css('width','100%');
|
||||
this.uiSelect.width(m[1]);
|
||||
this.uiWidth = null;
|
||||
} else {
|
||||
this.uiSelect.width(this.uiWidth);
|
||||
}
|
||||
["Right","Left"].forEach(function(d) {
|
||||
var m = that.element.css("margin"+d);
|
||||
that.uiSelect.css("margin"+d,m);
|
||||
that.element.css("margin"+d,0);
|
||||
});
|
||||
this.uiSelect.addClass("red-ui-typedInput-container");
|
||||
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
|
||||
this.selectTrigger = $('<button tabindex="0"></button>').prependTo(this.uiSelect);
|
||||
$('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
|
||||
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
|
||||
|
||||
this.types(this.options.types);
|
||||
|
||||
if (this.options.typeField) {
|
||||
this.typeField = $(this.options.typeField).hide();
|
||||
var t = this.typeField.val();
|
||||
if (t && this.typeMap[t]) {
|
||||
this.options.default = t;
|
||||
}
|
||||
} else {
|
||||
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
|
||||
}
|
||||
|
||||
this.element.on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('blur', function() {
|
||||
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('change', function() {
|
||||
that.validate();
|
||||
})
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
that._showTypeMenu();
|
||||
});
|
||||
this.selectTrigger.on('keydown',function(evt) {
|
||||
if (evt.keyCode === 40) {
|
||||
// Down
|
||||
that._showTypeMenu();
|
||||
}
|
||||
}).on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
})
|
||||
|
||||
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
|
||||
this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="fa fa-sort-desc"></i></span></button>').appendTo(this.uiSelect);
|
||||
this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
|
||||
this.optionSelectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
that._showOptionSelectMenu();
|
||||
}).on('keydown', function(evt) {
|
||||
if (evt.keyCode === 40) {
|
||||
// Down
|
||||
that._showOptionSelectMenu();
|
||||
}
|
||||
}).on('blur', function() {
|
||||
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
||||
}).on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
});
|
||||
|
||||
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"><i class="fa fa-ellipsis-h"></i></button>').appendTo(this.uiSelect);
|
||||
|
||||
|
||||
this.type(this.options.default||this.typeList[0].value);
|
||||
},
|
||||
_showTypeMenu: function() {
|
||||
if (this.typeList.length > 1) {
|
||||
this._showMenu(this.menu,this.selectTrigger);
|
||||
this.menu.find("[value='"+this.propertyType+"']").focus();
|
||||
} else {
|
||||
this.element.focus();
|
||||
}
|
||||
},
|
||||
_showOptionSelectMenu: function() {
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.css({
|
||||
minWidth:this.optionSelectLabel.width()
|
||||
});
|
||||
|
||||
this._showMenu(this.optionMenu,this.optionSelectLabel);
|
||||
var selectedOption = this.optionMenu.find("[value='"+this.value()+"']");
|
||||
if (selectedOption.length === 0) {
|
||||
selectedOption = this.optionMenu.children(":first");
|
||||
}
|
||||
selectedOption.focus();
|
||||
|
||||
}
|
||||
},
|
||||
_hideMenu: function(menu) {
|
||||
$(document).off("mousedown.close-property-select");
|
||||
menu.hide();
|
||||
if (this.elementDiv.is(":visible")) {
|
||||
this.element.focus();
|
||||
} else if (this.optionSelectTrigger.is(":visible")){
|
||||
this.optionSelectTrigger.focus();
|
||||
} else {
|
||||
this.selectTrigger.focus();
|
||||
}
|
||||
},
|
||||
_createMenu: function(opts,callback) {
|
||||
var that = this;
|
||||
var menu = $("<div>").addClass("red-ui-typedInput-options");
|
||||
opts.forEach(function(opt) {
|
||||
if (typeof opt === 'string') {
|
||||
opt = {value:opt,label:opt};
|
||||
}
|
||||
var op = $('<a href="#"></a>').attr("value",opt.value).appendTo(menu);
|
||||
if (opt.label) {
|
||||
op.text(opt.label);
|
||||
}
|
||||
if (opt.icon) {
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
|
||||
} else {
|
||||
op.css({paddingLeft: "18px"});
|
||||
}
|
||||
|
||||
op.click(function(event) {
|
||||
event.preventDefault();
|
||||
callback(opt.value);
|
||||
that._hideMenu(menu);
|
||||
});
|
||||
});
|
||||
menu.css({
|
||||
display: "none",
|
||||
});
|
||||
menu.appendTo(document.body);
|
||||
|
||||
menu.on('keydown', function(evt) {
|
||||
if (evt.keyCode === 40) {
|
||||
// DOWN
|
||||
$(this).children(":focus").next().focus();
|
||||
} else if (evt.keyCode === 38) {
|
||||
// UP
|
||||
$(this).children(":focus").prev().focus();
|
||||
} else if (evt.keyCode === 27) {
|
||||
that._hideMenu(menu);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
return menu;
|
||||
|
||||
},
|
||||
_showMenu: function(menu,relativeTo) {
|
||||
if (this.disarmClick) {
|
||||
this.disarmClick = false;
|
||||
return
|
||||
}
|
||||
var that = this;
|
||||
var pos = relativeTo.offset();
|
||||
var height = relativeTo.height();
|
||||
var menuHeight = menu.height();
|
||||
var top = (height+pos.top-3);
|
||||
if (top+menuHeight > $(window).height()) {
|
||||
top -= (top+menuHeight)-$(window).height()+5;
|
||||
}
|
||||
menu.css({
|
||||
top: top+"px",
|
||||
left: (2+pos.left)+"px",
|
||||
});
|
||||
menu.slideDown(100);
|
||||
this._delay(function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
$(document).on("mousedown.close-property-select", function(event) {
|
||||
if(!$(event.target).closest(menu).length) {
|
||||
that._hideMenu(menu);
|
||||
}
|
||||
if ($(event.target).closest(relativeTo).length) {
|
||||
that.disarmClick = true;
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
_getLabelWidth: function(label) {
|
||||
var labelWidth = label.outerWidth();
|
||||
if (labelWidth === 0) {
|
||||
var container = $('<div class="red-ui-typedInput-container"></div>').css({
|
||||
position:"absolute",
|
||||
top:0,
|
||||
left:-1000
|
||||
}).appendTo(document.body);
|
||||
var newTrigger = label.clone().appendTo(container);
|
||||
labelWidth = newTrigger.outerWidth();
|
||||
container.remove();
|
||||
}
|
||||
return labelWidth;
|
||||
},
|
||||
_resize: function() {
|
||||
if (this.uiWidth !== null) {
|
||||
this.uiSelect.width(this.uiWidth);
|
||||
}
|
||||
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
|
||||
this.selectTrigger.addClass("red-ui-typedInput-full-width");
|
||||
} else {
|
||||
this.selectTrigger.removeClass("red-ui-typedInput-full-width");
|
||||
var labelWidth = this._getLabelWidth(this.selectTrigger);
|
||||
this.elementDiv.css('left',labelWidth+"px");
|
||||
if (this.optionExpandButton.is(":visible")) {
|
||||
this.elementDiv.css('right',"22px");
|
||||
} else {
|
||||
this.elementDiv.css('right','0');
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'});
|
||||
}
|
||||
}
|
||||
},
|
||||
_destroy: function() {
|
||||
this.menu.remove();
|
||||
},
|
||||
types: function(types) {
|
||||
var that = this;
|
||||
var currentType = this.type();
|
||||
this.typeMap = {};
|
||||
this.typeList = types.map(function(opt) {
|
||||
var result;
|
||||
if (typeof opt === 'string') {
|
||||
result = allOptions[opt];
|
||||
} else {
|
||||
result = opt;
|
||||
}
|
||||
that.typeMap[result.value] = result;
|
||||
return result;
|
||||
});
|
||||
this.selectTrigger.toggleClass("disabled", this.typeList.length === 1);
|
||||
if (this.menu) {
|
||||
this.menu.remove();
|
||||
}
|
||||
this.menu = this._createMenu(this.typeList, function(v) { that.type(v) });
|
||||
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
||||
this.type(this.typeList[0].value);
|
||||
}
|
||||
},
|
||||
width: function(desiredWidth) {
|
||||
this.uiWidth = desiredWidth;
|
||||
this._resize();
|
||||
},
|
||||
value: function(value) {
|
||||
if (!arguments.length) {
|
||||
return this.element.val();
|
||||
} else {
|
||||
if (this.typeMap[this.propertyType].options) {
|
||||
if (this.typeMap[this.propertyType].options.indexOf(value) === -1) {
|
||||
value = "";
|
||||
}
|
||||
this.optionSelectLabel.text(value);
|
||||
}
|
||||
this.element.val(value);
|
||||
this.element.trigger('change',this.type(),value);
|
||||
}
|
||||
},
|
||||
type: function(type) {
|
||||
if (!arguments.length) {
|
||||
return this.propertyType;
|
||||
} else {
|
||||
var that = this;
|
||||
var opt = this.typeMap[type];
|
||||
if (opt && this.propertyType !== type) {
|
||||
this.propertyType = type;
|
||||
this.typeField.val(type);
|
||||
this.selectLabel.empty();
|
||||
var image;
|
||||
if (opt.icon) {
|
||||
image = new Image();
|
||||
image.name = opt.icon;
|
||||
image.src = opt.icon;
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
|
||||
} else {
|
||||
this.selectLabel.text(opt.label);
|
||||
}
|
||||
if (opt.options) {
|
||||
if (this.optionExpandButton) {
|
||||
this.optionExpandButton.hide();
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.show();
|
||||
this.elementDiv.hide();
|
||||
this.optionMenu = this._createMenu(opt.options,function(v){
|
||||
that.optionSelectLabel.text(v);
|
||||
that.value(v);
|
||||
});
|
||||
var currentVal = this.element.val();
|
||||
if (opt.options.indexOf(currentVal) !== -1) {
|
||||
this.optionSelectLabel.text(currentVal);
|
||||
} else {
|
||||
this.value(opt.options[0]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.remove();
|
||||
this.optionMenu = null;
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.hide();
|
||||
}
|
||||
if (opt.hasValue === false) {
|
||||
this.oldValue = this.element.val();
|
||||
this.element.val("");
|
||||
this.elementDiv.hide();
|
||||
} else {
|
||||
if (this.oldValue !== undefined) {
|
||||
this.element.val(this.oldValue);
|
||||
delete this.oldValue;
|
||||
}
|
||||
this.elementDiv.show();
|
||||
}
|
||||
if (opt.expand && typeof opt.expand === 'function') {
|
||||
this.optionExpandButton.show();
|
||||
this.optionExpandButton.off('click');
|
||||
this.optionExpandButton.on('click',function(evt) {
|
||||
evt.preventDefault();
|
||||
opt.expand.call(that);
|
||||
})
|
||||
} else {
|
||||
this.optionExpandButton.hide();
|
||||
}
|
||||
this.element.trigger('change',this.propertyType,this.value());
|
||||
}
|
||||
if (image) {
|
||||
image.onload = function() { that._resize(); }
|
||||
image.onerror = function() { that._resize(); }
|
||||
} else {
|
||||
this._resize();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
var result;
|
||||
var value = this.value();
|
||||
var type = this.type();
|
||||
if (this.typeMap[type] && this.typeMap[type].validate) {
|
||||
var val = this.typeMap[type].validate;
|
||||
if (typeof val === 'function') {
|
||||
result = val(value);
|
||||
} else {
|
||||
result = val.test(value);
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
if (result) {
|
||||
this.uiSelect.removeClass('input-error');
|
||||
} else {
|
||||
this.uiSelect.addClass('input-error');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
show: function() {
|
||||
this.uiSelect.show();
|
||||
this._resize();
|
||||
},
|
||||
hide: function() {
|
||||
this.uiSelect.hide();
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
394
editor/js/ui/deploy.js
Normal file
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.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";
|
||||
|
||||
var currentDiff = null;
|
||||
|
||||
function changeDeploymentType(type) {
|
||||
deploymentType = type;
|
||||
$("#btn-deploy-icon").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="#">'+
|
||||
'<span class="deploy-button-content">'+
|
||||
'<img id="btn-deploy-icon" src="red/images/deploy-full-o.png"> '+
|
||||
'<span>'+RED._("deploy.deploy")+'</span>'+
|
||||
'</span>'+
|
||||
'<span class="deploy-button-spinner hide">'+
|
||||
'<img src="red/images/spin.svg"/>'+
|
||||
'</span>'+
|
||||
'</a>'+
|
||||
'<a id="btn-deploy-options" data-toggle="dropdown" class="deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
RED.menu.init({id:"btn-deploy-options",
|
||||
options: [
|
||||
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.png",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
|
||||
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.png",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
|
||||
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.png",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}}
|
||||
]
|
||||
});
|
||||
} else if (type == "simple") {
|
||||
var label = options.label || RED._("deploy.deploy");
|
||||
var icon = 'red/images/deploy-full-o.png';
|
||||
if (options.hasOwnProperty('icon')) {
|
||||
icon = options.icon;
|
||||
}
|
||||
|
||||
$('<li><span class="deploy-button-group button-group">'+
|
||||
'<a id="btn-deploy" class="deploy-button disabled" href="#">'+
|
||||
'<span class="deploy-button-content">'+
|
||||
(icon?'<img id="btn-deploy-icon" src="'+icon+'"> ':'')+
|
||||
'<span>'+label+'</span>'+
|
||||
'</span>'+
|
||||
'<span class="deploy-button-spinner hide">'+
|
||||
'<img src="red/images/spin.svg"/>'+
|
||||
'</span>'+
|
||||
'</a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
}
|
||||
|
||||
$('#btn-deploy').click(function() { save(); });
|
||||
|
||||
RED.actions.add("core:deploy-flows",save);
|
||||
|
||||
$( "#node-dialog-confirm-deploy" ).dialog({
|
||||
title: RED._('deploy.confirm.button.confirm'),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 550,
|
||||
height: "auto",
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-dialog-confirm-deploy-review",
|
||||
text: RED._("deploy.confirm.button.review"),
|
||||
class: "primary disabled",
|
||||
click: function() {
|
||||
if (!$("#node-dialog-confirm-deploy-review").hasClass('disabled')) {
|
||||
RED.diff.showRemoteDiff();
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-dialog-confirm-deploy-merge",
|
||||
text: RED._("deploy.confirm.button.merge"),
|
||||
class: "primary disabled",
|
||||
click: function() {
|
||||
RED.diff.mergeDiff(currentDiff);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-dialog-confirm-deploy-deploy",
|
||||
text: RED._("deploy.confirm.button.confirm"),
|
||||
class: "primary",
|
||||
click: function() {
|
||||
|
||||
var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked");
|
||||
if (ignoreChecked) {
|
||||
ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true;
|
||||
}
|
||||
save(true,$( "#node-dialog-confirm-deploy-type" ).val() === "conflict");
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
create: function() {
|
||||
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
|
||||
.prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
|
||||
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
|
||||
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
|
||||
'<input type="hidden" id="node-dialog-confirm-deploy-type">'+
|
||||
'</div>');
|
||||
},
|
||||
open: function() {
|
||||
if ($( "#node-dialog-confirm-deploy-type" ).val() === "conflict") {
|
||||
$("#node-dialog-confirm-deploy-deploy").hide();
|
||||
$("#node-dialog-confirm-deploy-review").addClass('disabled').show();
|
||||
$("#node-dialog-confirm-deploy-merge").addClass('disabled').show();
|
||||
currentDiff = null;
|
||||
$("#node-dialog-confirm-deploy-conflict-checking").show();
|
||||
$("#node-dialog-confirm-deploy-conflict-auto-merge").hide();
|
||||
$("#node-dialog-confirm-deploy-conflict-manual-merge").hide();
|
||||
|
||||
var now = Date.now();
|
||||
RED.diff.getRemoteDiff(function(diff) {
|
||||
var ellapsed = Math.max(1000 - (Date.now()-now), 0);
|
||||
currentDiff = diff;
|
||||
setTimeout(function() {
|
||||
$("#node-dialog-confirm-deploy-conflict-checking").hide();
|
||||
var d = Object.keys(diff.conflicts);
|
||||
if (d.length === 0) {
|
||||
$("#node-dialog-confirm-deploy-conflict-auto-merge").show();
|
||||
$("#node-dialog-confirm-deploy-merge").removeClass('disabled')
|
||||
} else {
|
||||
$("#node-dialog-confirm-deploy-conflict-manual-merge").show();
|
||||
}
|
||||
$("#node-dialog-confirm-deploy-review").removeClass('disabled')
|
||||
},ellapsed);
|
||||
})
|
||||
|
||||
|
||||
$("#node-dialog-confirm-deploy-hide").parent().hide();
|
||||
} else {
|
||||
$("#node-dialog-confirm-deploy-deploy").show();
|
||||
$("#node-dialog-confirm-deploy-review").hide();
|
||||
$("#node-dialog-confirm-deploy-merge").hide();
|
||||
$("#node-dialog-confirm-deploy-hide").parent().show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RED.events.on('nodes:change',function(state) {
|
||||
if (state.dirty) {
|
||||
window.onbeforeunload = function() {
|
||||
return RED._("deploy.confirm.undeployedChanges");
|
||||
}
|
||||
$("#btn-deploy").removeClass("disabled");
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
$("#btn-deploy").addClass("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getNodeInfo(node) {
|
||||
var tabLabel = "";
|
||||
if (node.z) {
|
||||
var tab = RED.nodes.workspace(node.z);
|
||||
if (!tab) {
|
||||
tab = RED.nodes.subflow(node.z);
|
||||
tabLabel = tab.name;
|
||||
} else {
|
||||
tabLabel = tab.label;
|
||||
}
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
try {
|
||||
label = node._def.label.call(node);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node_def.type+".label",err);
|
||||
label = node_def.type;
|
||||
}
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
return {tab:tabLabel,type:node.type,label:label};
|
||||
}
|
||||
function sortNodeInfo(A,B) {
|
||||
if (A.tab < B.tab) { return -1;}
|
||||
if (A.tab > B.tab) { return 1;}
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
if (A.name < B.name) { return -1;}
|
||||
if (A.name > B.name) { return 1;}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function resolveConflict(currentNodes) {
|
||||
$( "#node-dialog-confirm-deploy-config" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unused" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-conflict" ).show();
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("conflict");
|
||||
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
|
||||
}
|
||||
|
||||
function save(skipValidation,force) {
|
||||
if (!$("#btn-deploy").hasClass("disabled")) {
|
||||
if (!skipValidation) {
|
||||
var hasUnknown = false;
|
||||
var hasInvalid = false;
|
||||
var hasUnusedConfig = false;
|
||||
|
||||
var unknownNodes = [];
|
||||
var invalidNodes = [];
|
||||
|
||||
RED.nodes.eachNode(function(node) {
|
||||
hasInvalid = hasInvalid || !node.valid;
|
||||
if (!node.valid) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
if (unknownNodes.indexOf(node.name) == -1) {
|
||||
unknownNodes.push(node.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
hasUnknown = unknownNodes.length > 0;
|
||||
|
||||
var unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (node.users.length === 0 && (node._def.hasUsers !== false)) {
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
});
|
||||
|
||||
$( "#node-dialog-confirm-deploy-config" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unused" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-conflict" ).hide();
|
||||
|
||||
var showWarning = false;
|
||||
|
||||
if (hasUnknown && !ignoreDeployWarnings.unknown) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("unknown");
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).show();
|
||||
$( "#node-dialog-confirm-deploy-unknown-list" )
|
||||
.html("<li>"+unknownNodes.join("</li><li>")+"</li>");
|
||||
} else if (hasInvalid && !ignoreDeployWarnings.invalid) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("invalid");
|
||||
$( "#node-dialog-confirm-deploy-config" ).show();
|
||||
invalidNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-invalid-list" )
|
||||
.html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
|
||||
} else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
|
||||
// showWarning = true;
|
||||
// $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
// $( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
//
|
||||
// unusedConfigNodes.sort(sortNodeInfo);
|
||||
// $( "#node-dialog-confirm-deploy-unused-list" )
|
||||
// .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
}
|
||||
if (showWarning) {
|
||||
$( "#node-dialog-confirm-deploy-hide" ).prop("checked",false);
|
||||
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var nns = RED.nodes.createCompleteNodeSet();
|
||||
|
||||
var startTime = Date.now();
|
||||
$(".deploy-button-content").css('opacity',0);
|
||||
$(".deploy-button-spinner").show();
|
||||
$("#btn-deploy").addClass("disabled");
|
||||
|
||||
var data = {flows:nns};
|
||||
|
||||
if (!force) {
|
||||
data.rev = RED.nodes.version();
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url:"flows",
|
||||
type: "POST",
|
||||
data: JSON.stringify(data),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
headers: {
|
||||
"Node-RED-Deployment-Type":deploymentType
|
||||
}
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.nodes.dirty(false);
|
||||
RED.nodes.version(data.rev);
|
||||
RED.nodes.originalFlow(nns);
|
||||
if (hasUnusedConfig) {
|
||||
RED.notify(
|
||||
'<p>'+RED._("deploy.successfulDeploy")+'</p>'+
|
||||
'<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
|
||||
} else {
|
||||
RED.notify(RED._("deploy.successfulDeploy"),"success");
|
||||
}
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
}
|
||||
if(node.credentials) {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function (confNode) {
|
||||
confNode.changed = false;
|
||||
if (confNode.credentials) {
|
||||
delete confNode.credentials;
|
||||
}
|
||||
});
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
ws.changed = false;
|
||||
})
|
||||
// Once deployed, cannot undo back to a clean state
|
||||
RED.history.markAllDirty();
|
||||
RED.view.redraw();
|
||||
RED.events.emit("deploy");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.nodes.dirty(true);
|
||||
$("#btn-deploy").removeClass("disabled");
|
||||
if (xhr.status === 401) {
|
||||
RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error");
|
||||
} else if (xhr.status === 409) {
|
||||
resolveConflict(nns);
|
||||
} else if (xhr.responseText) {
|
||||
RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error");
|
||||
} else {
|
||||
RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
|
||||
}
|
||||
}).always(function() {
|
||||
var delta = Math.max(0,300-(Date.now()-startTime));
|
||||
setTimeout(function() {
|
||||
$(".deploy-button-content").css('opacity',1);
|
||||
$(".deploy-button-spinner").hide();
|
||||
},delta);
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
})();
|
1278
editor/js/ui/diff.js
Normal file
1589
editor/js/ui/editor.js
Normal file
345
editor/js/ui/keyboard.js
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.keyboard = (function() {
|
||||
|
||||
var isMac = /Mac/i.test(window.navigator.platform);
|
||||
|
||||
var handlers = {};
|
||||
var partialState;
|
||||
|
||||
var keyMap = {
|
||||
"left":37,
|
||||
"up":38,
|
||||
"right":39,
|
||||
"down":40,
|
||||
"escape":27,
|
||||
"enter": 13,
|
||||
"backspace": 8,
|
||||
"delete": 46,
|
||||
"space": 32,
|
||||
";":186,
|
||||
"=":187,
|
||||
",":188,
|
||||
"-":189,
|
||||
".":190,
|
||||
"/":191,
|
||||
"\\":220,
|
||||
"'":222,
|
||||
"?":191 // <- QWERTY specific
|
||||
}
|
||||
var metaKeyCodes = {
|
||||
16:true,
|
||||
17:true,
|
||||
18: true,
|
||||
91:true,
|
||||
93: true
|
||||
}
|
||||
var actionToKeyMap = {}
|
||||
|
||||
// FF generates some different keycodes because reasons.
|
||||
var firefoxKeyCodeMap = {
|
||||
59:186,
|
||||
61:187,
|
||||
173:189
|
||||
}
|
||||
|
||||
function init() {
|
||||
$.getJSON("red/keymap.json",function(data) {
|
||||
for (var scope in data) {
|
||||
if (data.hasOwnProperty(scope)) {
|
||||
var keys = data[scope];
|
||||
for (var key in keys) {
|
||||
if (keys.hasOwnProperty(key)) {
|
||||
addHandler(scope,key,keys[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
RED.actions.add("core:show-help", showKeyboardHelp);
|
||||
|
||||
}
|
||||
function parseKeySpecifier(key) {
|
||||
var parts = key.toLowerCase().split("-");
|
||||
var modifiers = {};
|
||||
var keycode;
|
||||
var blank = 0;
|
||||
for (var i=0;i<parts.length;i++) {
|
||||
switch(parts[i]) {
|
||||
case "ctrl":
|
||||
case "cmd":
|
||||
modifiers.ctrl = true;
|
||||
modifiers.meta = true;
|
||||
break;
|
||||
case "alt":
|
||||
modifiers.alt = true;
|
||||
break;
|
||||
case "shift":
|
||||
modifiers.shift = true;
|
||||
break;
|
||||
case "":
|
||||
blank++;
|
||||
keycode = keyMap["-"];
|
||||
break;
|
||||
default:
|
||||
if (keyMap.hasOwnProperty(parts[i])) {
|
||||
keycode = keyMap[parts[i]];
|
||||
} else if (parts[i].length > 1) {
|
||||
return null;
|
||||
} else {
|
||||
keycode = parts[i].toUpperCase().charCodeAt(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [keycode,modifiers];
|
||||
}
|
||||
|
||||
function resolveKeyEvent(evt) {
|
||||
var slot = partialState||handlers;
|
||||
if (evt.ctrlKey || evt.metaKey) {
|
||||
slot = slot.ctrl;
|
||||
}
|
||||
if (slot && evt.shiftKey) {
|
||||
slot = slot.shift;
|
||||
}
|
||||
if (slot && evt.altKey) {
|
||||
slot = slot.alt;
|
||||
}
|
||||
var keyCode = firefoxKeyCodeMap[evt.keyCode] || evt.keyCode;
|
||||
if (slot && slot[keyCode]) {
|
||||
var handler = slot[keyCode];
|
||||
if (!handler.scope) {
|
||||
if (partialState) {
|
||||
partialState = null;
|
||||
return resolveKeyEvent(evt);
|
||||
} else {
|
||||
partialState = handler;
|
||||
evt.preventDefault();
|
||||
return null;
|
||||
}
|
||||
} else if (handler.scope && handler.scope !== "*") {
|
||||
var target = evt.target;
|
||||
while (target.nodeName !== 'BODY' && target.id !== handler.scope) {
|
||||
target = target.parentElement;
|
||||
}
|
||||
if (target.nodeName === 'BODY') {
|
||||
handler = null;
|
||||
}
|
||||
}
|
||||
partialState = null;
|
||||
return handler;
|
||||
} else if (partialState) {
|
||||
partialState = null;
|
||||
return resolveKeyEvent(evt);
|
||||
}
|
||||
}
|
||||
d3.select(window).on("keydown",function() {
|
||||
if (metaKeyCodes[d3.event.keyCode]) {
|
||||
return;
|
||||
}
|
||||
var handler = resolveKeyEvent(d3.event);
|
||||
if (handler && handler.ondown) {
|
||||
if (typeof handler.ondown === "string") {
|
||||
RED.actions.invoke(handler.ondown);
|
||||
} else {
|
||||
handler.ondown();
|
||||
}
|
||||
d3.event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
function addHandler(scope,key,modifiers,ondown) {
|
||||
var mod = modifiers;
|
||||
var cbdown = ondown;
|
||||
if (typeof modifiers == "function" || typeof modifiers === "string") {
|
||||
mod = {};
|
||||
cbdown = modifiers;
|
||||
}
|
||||
var keys = [];
|
||||
var i=0;
|
||||
if (typeof key === 'string') {
|
||||
if (typeof cbdown === 'string') {
|
||||
actionToKeyMap[cbdown] = {scope:scope,key:key};
|
||||
}
|
||||
var parts = key.split(" ");
|
||||
for (i=0;i<parts.length;i++) {
|
||||
var parsedKey = parseKeySpecifier(parts[i]);
|
||||
if (parsedKey) {
|
||||
keys.push(parsedKey);
|
||||
} else {
|
||||
console.log("Unrecognised key specifier:",key)
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keys.push([key,mod])
|
||||
}
|
||||
var slot = handlers;
|
||||
for (i=0;i<keys.length;i++) {
|
||||
key = keys[i][0];
|
||||
mod = keys[i][1];
|
||||
if (mod.ctrl) {
|
||||
slot.ctrl = slot.ctrl||{};
|
||||
slot = slot.ctrl;
|
||||
}
|
||||
if (mod.shift) {
|
||||
slot.shift = slot.shift||{};
|
||||
slot = slot.shift;
|
||||
}
|
||||
if (mod.alt) {
|
||||
slot.alt = slot.alt||{};
|
||||
slot = slot.alt;
|
||||
}
|
||||
slot[key] = slot[key] || {};
|
||||
slot = slot[key];
|
||||
//slot[key] = {scope: scope, ondown:cbdown};
|
||||
}
|
||||
slot.scope = scope;
|
||||
slot.ondown = cbdown;
|
||||
}
|
||||
|
||||
function removeHandler(key,modifiers) {
|
||||
var mod = modifiers || {};
|
||||
var keys = [];
|
||||
var i=0;
|
||||
if (typeof key === 'string') {
|
||||
delete actionToKeyMap[key];
|
||||
var parts = key.split(" ");
|
||||
for (i=0;i<parts.length;i++) {
|
||||
var parsedKey = parseKeySpecifier(parts[i]);
|
||||
if (parsedKey) {
|
||||
keys.push(parsedKey);
|
||||
} else {
|
||||
console.log("Unrecognised key specifier:",key)
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keys.push([key,mod])
|
||||
}
|
||||
var slot = handlers;
|
||||
for (i=0;i<keys.length;i++) {
|
||||
key = keys[i][0];
|
||||
mod = keys[i][1];
|
||||
if (mod.ctrl) {
|
||||
slot = slot.ctrl;
|
||||
}
|
||||
if (slot && mod.shift) {
|
||||
slot = slot.shift;
|
||||
}
|
||||
if (slot && mod.alt) {
|
||||
slot = slot.alt;
|
||||
}
|
||||
if (!slot[key]) {
|
||||
return;
|
||||
}
|
||||
slot = slot[key];
|
||||
}
|
||||
delete slot.scope;
|
||||
delete slot.ondown;
|
||||
}
|
||||
|
||||
var shortcutDialog = null;
|
||||
|
||||
var cmdCtrlKey = '<span class="help-key">'+(isMac?'⌘':'Ctrl')+'</span>';
|
||||
|
||||
function showKeyboardHelp() {
|
||||
if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) {
|
||||
return;
|
||||
}
|
||||
if (!shortcutDialog) {
|
||||
shortcutDialog = $('<div id="keyboard-help-dialog" class="hide">'+
|
||||
'<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
|
||||
'<div class="keyboard-shortcut-entry-key">shortcut</div>'+
|
||||
'<div class="keyboard-shortcut-entry-key">action</div>'+
|
||||
'<div class="keyboard-shortcut-entry-scope">scope</div>'+
|
||||
'</div>'+
|
||||
'<ol id="keyboard-shortcut-list"></ol>'+
|
||||
'</div>')
|
||||
.appendTo("body");
|
||||
|
||||
var shortcutList = $('#keyboard-shortcut-list').editableList({
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
addItem: function(container,i,object) {
|
||||
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
|
||||
|
||||
var key = $('<div class="keyboard-shortcut-entry-key">').appendTo(item);
|
||||
if (object.key) {
|
||||
key.append(formatKey(object.key));
|
||||
} else {
|
||||
item.addClass("keyboard-shortcut-entry-unassigned");
|
||||
key.html(RED._('keyboard.unassigned'));
|
||||
}
|
||||
|
||||
var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() {
|
||||
if (arguments[5] === 0) {
|
||||
return arguments[2].toUpperCase();
|
||||
} else {
|
||||
return " "+arguments[4].toUpperCase();
|
||||
}
|
||||
});
|
||||
var label = $('<div>').html(text).appendTo(item);
|
||||
if (object.scope) {
|
||||
$('<div class="keyboard-shortcut-entry-scope">').html(object.scope).appendTo(item);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
var shortcuts = RED.actions.list();
|
||||
shortcuts.sort(function(A,B) {
|
||||
return A.id.localeCompare(B.id);
|
||||
});
|
||||
shortcuts.forEach(function(s) {
|
||||
shortcutList.editableList('addItem',s);
|
||||
})
|
||||
|
||||
shortcutDialog.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: "800",
|
||||
height: "400",
|
||||
title:RED._("keyboard.title"),
|
||||
resizable: false
|
||||
});
|
||||
}
|
||||
|
||||
shortcutDialog.dialog("open");
|
||||
}
|
||||
function formatKey(key) {
|
||||
var formattedKey = isMac?key.replace(/ctrl-?/,"⌘"):key;
|
||||
formattedKey = isMac?formattedKey.replace(/alt-?/,"⌥"):key;
|
||||
formattedKey = formattedKey.replace(/shift-?/,"⇧")
|
||||
formattedKey = formattedKey.replace(/left/,"←")
|
||||
formattedKey = formattedKey.replace(/up/,"↑")
|
||||
formattedKey = formattedKey.replace(/right/,"→")
|
||||
formattedKey = formattedKey.replace(/down/,"↓")
|
||||
return '<span class="help-key-block"><span class="help-key">'+formattedKey.split(" ").join('</span> <span class="help-key">')+'</span></span>';
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add: addHandler,
|
||||
remove: removeHandler,
|
||||
getShortcut: function(actionName) {
|
||||
return actionToKeyMap[actionName];
|
||||
},
|
||||
formatKey: formatKey
|
||||
}
|
||||
|
||||
})();
|