mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-07-23 16:11:24 +00:00
Compare commits
1386 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f28803a82 | ||
|
|
943bdecd3e | ||
|
|
e4ff4a78cc | ||
|
|
fec5538f01 | ||
|
|
b7bb363aa8 | ||
|
|
cd4a2cec76 | ||
|
|
0638ca32cb | ||
|
|
fe185cc93e | ||
|
|
7edea50296 | ||
|
|
8258cdb651 | ||
|
|
a9e29eee85 | ||
|
|
a52a6aae62 | ||
|
|
92c79830f5 | ||
|
|
8629f67972 | ||
|
|
c04d363c46 | ||
|
|
046a4ef22e | ||
|
|
579c9e41d7 | ||
|
|
6014898610 | ||
|
|
ad3bf95e5f | ||
|
|
4e22a4edde | ||
|
|
9c50fe53e7 | ||
|
|
a2a2d51ec1 | ||
|
|
c36c32cb08 | ||
|
|
78b206e06b | ||
|
|
ef83a61a0c | ||
|
|
f5c0aa5c4c | ||
|
|
e59167b1fa | ||
|
|
ea584aceea | ||
|
|
4d7116b467 | ||
|
|
b149288545 | ||
|
|
9d06279ba8 | ||
|
|
21e1dcf0e5 | ||
|
|
b3b0c1d159 | ||
|
|
76d7bf4d73 | ||
|
|
f1d80282eb | ||
|
|
220cc5557b | ||
|
|
d2176c93fb | ||
|
|
c558e44a1c | ||
|
|
45ec062bce | ||
|
|
f8edddd442 | ||
|
|
fc3d5ac700 | ||
|
|
4ee38db68c | ||
|
|
1dfa7ca355 | ||
|
|
40ecef5ec4 | ||
|
|
cad5e0f44e | ||
|
|
d139011cc3 | ||
|
|
5d9628ce70 | ||
|
|
161bede8db | ||
|
|
2be395e640 | ||
|
|
447d990f43 | ||
|
|
4f4725ac3c | ||
|
|
eff03299b6 | ||
|
|
a6adf39db2 | ||
|
|
70ff6fb29f | ||
|
|
eb32fff688 | ||
|
|
6071b66118 | ||
|
|
83c0b8d3f1 | ||
|
|
79da2319f8 | ||
|
|
68eacb916a | ||
|
|
ccf608a52e | ||
|
|
4acd294bf6 | ||
|
|
05c800431d | ||
|
|
6ff1db2710 | ||
|
|
800a6545a1 | ||
|
|
a985bb36ca | ||
|
|
e7c55e05b8 | ||
|
|
4315ec6179 | ||
|
|
628d12fa2f | ||
|
|
d82d0335b2 | ||
|
|
acfa27cf74 | ||
|
|
14f009f832 | ||
|
|
d1f6476604 | ||
|
|
c6c6014691 | ||
|
|
9a6b89356e | ||
|
|
882fe83623 | ||
|
|
a9bc22ce29 | ||
|
|
36bacfe915 | ||
|
|
bcbef1a3b0 | ||
|
|
591da25cfe | ||
|
|
1e1b073c8c | ||
|
|
ea7b828cbb | ||
|
|
cd6c11ace2 | ||
|
|
c4beeec63b | ||
|
|
87c13e7cce | ||
|
|
d5cd11e812 | ||
|
|
dc94d2db7a | ||
|
|
1637beee78 | ||
|
|
0541b7e351 | ||
|
|
b1bc4e731f | ||
|
|
7c0dca6038 | ||
|
|
24fd0a8aa7 | ||
|
|
2b3dc1573d | ||
|
|
48693d9e24 | ||
|
|
977a8aaaf4 | ||
|
|
15537ec518 | ||
|
|
7d0098fd5c | ||
|
|
da0c4c4704 | ||
|
|
b0687b54c7 | ||
|
|
a89e901b2b | ||
|
|
ef06a0c310 | ||
|
|
e7242900a4 | ||
|
|
a6b01dce49 | ||
|
|
1cfc1a6f5d | ||
|
|
9915757dce | ||
|
|
673fba2e21 | ||
|
|
d37f8cd36e | ||
|
|
5e509a4d7b | ||
|
|
cb6cabd4c9 | ||
|
|
f61cc46f41 | ||
|
|
32a8f169d4 | ||
|
|
aa74123228 | ||
|
|
27f20d6af8 | ||
|
|
4695480ead | ||
|
|
bec3f5a799 | ||
|
|
13b15b7bea | ||
|
|
969bc770e3 | ||
|
|
5ecc48e348 | ||
|
|
9efefc23e0 | ||
|
|
6a08479a1b | ||
|
|
c2563ed6b3 | ||
|
|
46c655546e | ||
|
|
4fa0786a39 | ||
|
|
ae2ce3882c | ||
|
|
3c9389c4f0 | ||
|
|
bb6be7abbb | ||
|
|
3aac4e68b4 | ||
|
|
ea96d13fab | ||
|
|
f9c5804fb8 | ||
|
|
84f96fa185 | ||
|
|
8238bd4011 | ||
|
|
36cdef3717 | ||
|
|
f8035d6c49 | ||
|
|
845f9bd127 | ||
|
|
b5ec9ce210 | ||
|
|
71a1d78c39 | ||
|
|
6bbfeb9024 | ||
|
|
a038470816 | ||
|
|
45e0ff41f4 | ||
|
|
9970ff7629 | ||
|
|
4395a47178 | ||
|
|
77daf5028e | ||
|
|
c40ae8e405 | ||
|
|
f463c5c5a7 | ||
|
|
d1032f09ab | ||
|
|
b3e682c0a5 | ||
|
|
ffabdc5276 | ||
|
|
7787bfc22e | ||
|
|
406429aec8 | ||
|
|
32f89d3874 | ||
|
|
1c0021dbb6 | ||
|
|
df4fb51716 | ||
|
|
314cceb462 | ||
|
|
ceb7ef4ff7 | ||
|
|
34fad6ea73 | ||
|
|
d12e7bd0c5 | ||
|
|
b8a621ca1b | ||
|
|
1fc1e3734c | ||
|
|
582b7423dc | ||
|
|
e4d7d441fc | ||
|
|
e53aa06cdb | ||
|
|
b4345ed40d | ||
|
|
ddbf4d69a4 | ||
|
|
e2855641ca | ||
|
|
5c6a9a958e | ||
|
|
d2a7f6343a | ||
|
|
2b29448062 | ||
|
|
09a8b2bd1c | ||
|
|
6a52b11e46 | ||
|
|
9a053ec374 | ||
|
|
6aa496c995 | ||
|
|
738059f27f | ||
|
|
3fc8932f4c | ||
|
|
2db458bc7b | ||
|
|
cf424d6c05 | ||
|
|
8b9004b4dd | ||
|
|
efb24e229f | ||
|
|
19347a2b4c | ||
|
|
a80f109d19 | ||
|
|
74e008198f | ||
|
|
d25a08d2bd | ||
|
|
e04c459489 | ||
|
|
3d2944bd13 | ||
|
|
52a9076f14 | ||
|
|
7e2d2ede98 | ||
|
|
6c7709be9c | ||
|
|
340a639f1b | ||
|
|
aa5a772f00 | ||
|
|
439b3710cc | ||
|
|
e41828410c | ||
|
|
799d86bda4 | ||
|
|
a423739cb8 | ||
|
|
47acb8fdf7 | ||
|
|
1f95f18898 | ||
|
|
2aaf0a5385 | ||
|
|
1a44ab0eac | ||
|
|
fe3e201143 | ||
|
|
f0deb32362 | ||
|
|
ba6f66e73e | ||
|
|
cc7f9dc75b | ||
|
|
f671f79814 | ||
|
|
fa8875d53c | ||
|
|
2dd62241f7 | ||
|
|
4d1cd8cdc6 | ||
|
|
54b56fdc38 | ||
|
|
785c32e2e3 | ||
|
|
c3f2b7c32f | ||
|
|
110908e221 | ||
|
|
8be917da4a | ||
|
|
8db81f30dc | ||
|
|
56439d5ec2 | ||
|
|
e761cacbd0 | ||
|
|
0bb706995a | ||
|
|
69498ed6ab | ||
|
|
82f8d846a9 | ||
|
|
1ea4aa67ca | ||
|
|
8763788bad | ||
|
|
c1c67c18af | ||
|
|
09a02074e1 | ||
|
|
c448eaa48b | ||
|
|
05ad2fd635 | ||
|
|
b949a149ec | ||
|
|
6e5318faa7 | ||
|
|
ec66ba78a7 | ||
|
|
a1216cddeb | ||
|
|
2f0affd3a3 | ||
|
|
68b2aa780b | ||
|
|
56e21b4011 | ||
|
|
665ed9c2e9 | ||
|
|
a44a28f496 | ||
|
|
f51229fc6f | ||
|
|
0c9f3922f8 | ||
|
|
4410cd2665 | ||
|
|
618a561514 | ||
|
|
682729ac3e | ||
|
|
67f27d8425 | ||
|
|
97672ef5d5 | ||
|
|
e6684df59e | ||
|
|
9493e4de7e | ||
|
|
025b005e12 | ||
|
|
2d524a4974 | ||
|
|
5e0b681d8b | ||
|
|
b052cf220d | ||
|
|
4fa92f9394 | ||
|
|
02dbe56d79 | ||
|
|
e616cc0bc9 | ||
|
|
ecc76e8b1e | ||
|
|
0fbfa0f0cf | ||
|
|
53c072abe4 | ||
|
|
4b9922bf92 | ||
|
|
1bb77992b0 | ||
|
|
bec991d941 | ||
|
|
56fc8b838a | ||
|
|
df21e81d5a | ||
|
|
8b83277ec0 | ||
|
|
660f57e914 | ||
|
|
151c56f9a3 | ||
|
|
9e529605e1 | ||
|
|
ca97c21e9c | ||
|
|
01677e6a09 | ||
|
|
7e35c60677 | ||
|
|
a20640ed75 | ||
|
|
e5b221f7ac | ||
|
|
2396000a6a | ||
|
|
7661f69f1a | ||
|
|
708a4c6b99 | ||
|
|
84f3a3dc79 | ||
|
|
80613f46fa | ||
|
|
e9a8910c9c | ||
|
|
e78f5d943f | ||
|
|
10934b3ad9 | ||
|
|
ddf5a59f70 | ||
|
|
59037f2c18 | ||
|
|
1444550865 | ||
|
|
b0c1d36c81 | ||
|
|
10b225a337 | ||
|
|
289f67e0d3 | ||
|
|
daf8387516 | ||
|
|
170b4da211 | ||
|
|
c9b27bb514 | ||
|
|
5c19ddba61 | ||
|
|
b28a070a28 | ||
|
|
43c5a01102 | ||
|
|
15dcae5c5e | ||
|
|
5976da81df | ||
|
|
37cb83f2cf | ||
|
|
dd5f392293 | ||
|
|
15fab5142f | ||
|
|
d2801ca896 | ||
|
|
68c7d0be3b | ||
|
|
bfbaae9ec0 | ||
|
|
3987cc82c1 | ||
|
|
559b7424b6 | ||
|
|
964ae989f2 | ||
|
|
ff4e02c210 | ||
|
|
9ead35242a | ||
|
|
061ab38b24 | ||
|
|
07bc9e2782 | ||
|
|
a66eeda37e | ||
|
|
4340533acf | ||
|
|
394939e0d5 | ||
|
|
0857711f61 | ||
|
|
5aba36cca0 | ||
|
|
3d2e5feda2 | ||
|
|
321a54dd59 | ||
|
|
feb6eb2468 | ||
|
|
7c5ac408e5 | ||
|
|
37ecb3d7ac | ||
|
|
52023c82cb | ||
|
|
6d19bf2e14 | ||
|
|
d7214aba70 | ||
|
|
6e1cf4ef52 | ||
|
|
3aed798ff1 | ||
|
|
957fbc6533 | ||
|
|
b20539ef2b | ||
|
|
7ef134f46f | ||
|
|
cdeb40d191 | ||
|
|
1e18d51342 | ||
|
|
8836d816d5 | ||
|
|
ff9fcb1189 | ||
|
|
45cf9bafcd | ||
|
|
3e647290fd | ||
|
|
800ed012ba | ||
|
|
525c17d4bd | ||
|
|
4424749978 | ||
|
|
9fc3c389bf | ||
|
|
9ffe3ef137 | ||
|
|
a9105e6797 | ||
|
|
3438a0c6a1 | ||
|
|
e969b3c2b3 | ||
|
|
d274a2b3db | ||
|
|
5c9ae0a0de | ||
|
|
8fd79a2792 | ||
|
|
9267f7a28d | ||
|
|
3479c3b3f2 | ||
|
|
ac94eccd90 | ||
|
|
144e4a3468 | ||
|
|
938f9e7674 | ||
|
|
c229160605 | ||
|
|
fb40bd9cec | ||
|
|
76125be9f5 | ||
|
|
d0730e12d9 | ||
|
|
66a08c7f71 | ||
|
|
f3a0d3a3fe | ||
|
|
4a5a56f403 | ||
|
|
846dd1bf22 | ||
|
|
99d8435e9d | ||
|
|
30f42e9495 | ||
|
|
f84ce8534e | ||
|
|
a266c1452c | ||
|
|
b61818fe62 | ||
|
|
dad4cd1f57 | ||
|
|
f5cabc7696 | ||
|
|
6e3ba27131 | ||
|
|
e68338a17b | ||
|
|
c7095e2570 | ||
|
|
31570e4fae | ||
|
|
307583e374 | ||
|
|
09601047e5 | ||
|
|
3d19f4727a | ||
|
|
4533a25c6f | ||
|
|
e8a81c49ea | ||
|
|
071141bc5a | ||
|
|
1db64d7324 | ||
|
|
bb32f7c448 | ||
|
|
457c83dba9 | ||
|
|
f8a941bdd9 | ||
|
|
9d6cffafdc | ||
|
|
5b704d0684 | ||
|
|
2c2ca8eb39 | ||
|
|
c9f57c5f20 | ||
|
|
327ac9dee6 | ||
|
|
dc34ee8714 | ||
|
|
3cd3695be6 | ||
|
|
dfcbfd632e | ||
|
|
bf50550911 | ||
|
|
e3ad330ebc | ||
|
|
983fd9af3d | ||
|
|
ebabad948f | ||
|
|
c3b71653bb | ||
|
|
8cad7514e7 | ||
|
|
1f174870ab | ||
|
|
7173f6cb18 | ||
|
|
0d7b037ac2 | ||
|
|
dae57c3d20 | ||
|
|
c1014c9cc0 | ||
|
|
2e486f8bed | ||
|
|
6addb29f7f | ||
|
|
c0988590a7 | ||
|
|
5e742c5b82 | ||
|
|
2a0feac7e0 | ||
|
|
603e232ca2 | ||
|
|
73fa4961c0 | ||
|
|
ebfdf8c087 | ||
|
|
ad6831712b | ||
|
|
9e65c52ff1 | ||
|
|
0054cfa363 | ||
|
|
145bc48722 | ||
|
|
e4d0ece093 | ||
|
|
c704ffe47a | ||
|
|
5182cb8107 | ||
|
|
9543f1adb0 | ||
|
|
21d765fb9f | ||
|
|
f9c2831939 | ||
|
|
9d2c4e4a0d | ||
|
|
5a2aa70f90 | ||
|
|
dee50a8548 | ||
|
|
23c0d77042 | ||
|
|
cf2c641564 | ||
|
|
0eb802e7bd | ||
|
|
8223800ac6 | ||
|
|
041d889970 | ||
|
|
4c242631fa | ||
|
|
75938809ea | ||
|
|
265c0b491a | ||
|
|
9ab2cec877 | ||
|
|
82bb338c1f | ||
|
|
3bc49f02f8 | ||
|
|
4f1a797cd7 | ||
|
|
f8a63b5a13 | ||
|
|
d250bf0223 | ||
|
|
e1d2f01f57 | ||
|
|
2cc97f33f1 | ||
|
|
b5acbafab9 | ||
|
|
bf1ffd0751 | ||
|
|
41b5a96fa6 | ||
|
|
116430e542 | ||
|
|
27a52d33d0 | ||
|
|
54c89e52f8 | ||
|
|
6b049d954b | ||
|
|
5a16b018c5 | ||
|
|
f849b8423d | ||
|
|
091f901789 | ||
|
|
3c0c7c3044 | ||
|
|
3c1a82e702 | ||
|
|
6fe61b775b | ||
|
|
dc5867b006 | ||
|
|
77b70b2142 | ||
|
|
78e87796df | ||
|
|
75ad4415c4 | ||
|
|
c5d9bd7c1f | ||
|
|
f437a52ff0 | ||
|
|
7192a56e94 | ||
|
|
b9dd4f2676 | ||
|
|
e8cf3c92f9 | ||
|
|
d574a93d6d | ||
|
|
8a769fd512 | ||
|
|
3b409074c1 | ||
|
|
3b6b0b5a5b | ||
|
|
8876f050e0 | ||
|
|
8507282d55 | ||
|
|
f61b21eecc | ||
|
|
242820b7cf | ||
|
|
dd240ba9b2 | ||
|
|
4b945c0047 | ||
|
|
23a6a6981a | ||
|
|
9cebb2416f | ||
|
|
a94fa0d465 | ||
|
|
3bdf47a5bb | ||
|
|
0ccd109a50 | ||
|
|
6b0bd2e1de | ||
|
|
c23a6a12c8 | ||
|
|
a523ef65f5 | ||
|
|
0971ac4e94 | ||
|
|
64654e8d5c | ||
|
|
a063353a27 | ||
|
|
add31607f9 | ||
|
|
e36de00ce8 | ||
|
|
2c1210bbb1 | ||
|
|
85ebf21122 | ||
|
|
450f14614d | ||
|
|
f75bf4a401 | ||
|
|
d979e94927 | ||
|
|
8de9bd0847 | ||
|
|
e178321be9 | ||
|
|
4b84d20bb0 | ||
|
|
6e778a35ba | ||
|
|
e27ef51d43 | ||
|
|
917f585137 | ||
|
|
9950cacad9 | ||
|
|
457e23f31d | ||
|
|
cfa0b5e382 | ||
|
|
f903c5c9c5 | ||
|
|
08dd5ac183 | ||
|
|
9e2c5ac635 | ||
|
|
6856207d0d | ||
|
|
f0dcbe946f | ||
|
|
008279df57 | ||
|
|
dc99417c27 | ||
|
|
12751646fe | ||
|
|
8234201a39 | ||
|
|
4bb7d704b4 | ||
|
|
feb5aad36b | ||
|
|
c231fca334 | ||
|
|
14452d05b6 | ||
|
|
5c1ad5f8ff | ||
|
|
5b4bb90d61 | ||
|
|
5cc6f79d6a | ||
|
|
024bc41345 | ||
|
|
fb115c675e | ||
|
|
53f6f9e76f | ||
|
|
6d21c539d4 | ||
|
|
253389ea7b | ||
|
|
cdf11f7483 | ||
|
|
0ff16314d7 | ||
|
|
cc889bda4f | ||
|
|
43edba8cb8 | ||
|
|
06f3b78256 | ||
|
|
dfcd655780 | ||
|
|
ab20a462db | ||
|
|
2ec57132ed | ||
|
|
0222d177fc | ||
|
|
db0e0aef73 | ||
|
|
14ec19e80a | ||
|
|
e6a00979ff | ||
|
|
5b5352c7d9 | ||
|
|
78ddbd8a39 | ||
|
|
0067653109 | ||
|
|
5a2ac765d4 | ||
|
|
3ceb9a8c59 | ||
|
|
2a525e9bbb | ||
|
|
a49e963dc9 | ||
|
|
1037a1a264 | ||
|
|
8477f3e466 | ||
|
|
e5d004c0af | ||
|
|
836e908e11 | ||
|
|
9fd4a451ab | ||
|
|
f4386a6f0f | ||
|
|
91f9c0a1aa | ||
|
|
babeb9fb85 | ||
|
|
2e667ba693 | ||
|
|
07d18cfe96 | ||
|
|
60c56164b0 | ||
|
|
d8453236c2 | ||
|
|
efe81320e1 | ||
|
|
14e6754be3 | ||
|
|
3033aed2e3 | ||
|
|
5b972e2e4a | ||
|
|
543e08a837 | ||
|
|
2a8109f090 | ||
|
|
39a24028ac | ||
|
|
3f3c644500 | ||
|
|
38dd510158 | ||
|
|
64df81bc45 | ||
|
|
8366b3bbe2 | ||
|
|
66db798c35 | ||
|
|
56ed1cd535 | ||
|
|
c5aed16f63 | ||
|
|
1537fa73fc | ||
|
|
511347d3ef | ||
|
|
c963ffefee | ||
|
|
fc6304a1c6 | ||
|
|
a05ecdb406 | ||
|
|
9e4d66df4d | ||
|
|
3f7e17721e | ||
|
|
50b19dcc44 | ||
|
|
7fb130cdbd | ||
|
|
5970f10a84 | ||
|
|
84a51a932c | ||
|
|
b0360d8ef4 | ||
|
|
00a20129de | ||
|
|
5e657e9bd1 | ||
|
|
2559343eef | ||
|
|
ddf9e45567 | ||
|
|
84bfef562d | ||
|
|
2e7bdb8aa0 | ||
|
|
a2f66951a8 | ||
|
|
03631d95f7 | ||
|
|
1fc57fdb91 | ||
|
|
09408af5dd | ||
|
|
4eb47f9d42 | ||
|
|
4fcaf2885d | ||
|
|
eaea183536 | ||
|
|
c69ec16ef0 | ||
|
|
3cf13a92c0 | ||
|
|
08d13c6d6b | ||
|
|
df79dda4d0 | ||
|
|
b4792fc3f2 | ||
|
|
c164766895 | ||
|
|
0584f4f431 | ||
|
|
67e4b8f0e4 | ||
|
|
876bbfded5 | ||
|
|
15b71c1485 | ||
|
|
d252fe5fcb | ||
|
|
25119ac6ea | ||
|
|
fc20b9a4e1 | ||
|
|
faa2f9c765 | ||
|
|
08b5c6b2b5 | ||
|
|
83a1e527e5 | ||
|
|
a6d18ddb88 | ||
|
|
7db31363ff | ||
|
|
2077468a85 | ||
|
|
e3fcb7f24e | ||
|
|
64e4aba414 | ||
|
|
899266a62a | ||
|
|
d568a4d896 | ||
|
|
884c83eb60 | ||
|
|
b34ee44bb1 | ||
|
|
c3251bf5e1 | ||
|
|
54bbd4e34d | ||
|
|
3d13b9c5f8 | ||
|
|
61107329fc | ||
|
|
f0bc286aea | ||
|
|
8eb724b58c | ||
|
|
784bb2ebdd | ||
|
|
8f52376d3f | ||
|
|
ed31dd2409 | ||
|
|
8200874912 | ||
|
|
4b8568b264 | ||
|
|
de41aec77d | ||
|
|
74f5eb0cae | ||
|
|
1d983e9950 | ||
|
|
f9b3844153 | ||
|
|
546d061068 | ||
|
|
b1e231a5bb | ||
|
|
121b114e89 | ||
|
|
8592cedce0 | ||
|
|
a53ab67b7d | ||
|
|
de2cedd99d | ||
|
|
da545ea418 | ||
|
|
db6e8334b8 | ||
|
|
2f0c1a616c | ||
|
|
e100dcf627 | ||
|
|
76c4000890 | ||
|
|
fb5e5a320f | ||
|
|
8551cd54a6 | ||
|
|
c93803190b | ||
|
|
ddc94adbf1 | ||
|
|
ef42330848 | ||
|
|
a54caa1d7c | ||
|
|
9eafa189a8 | ||
|
|
baaf47e62f | ||
|
|
0d61a972fb | ||
|
|
af77eeed8d | ||
|
|
e99d084728 | ||
|
|
b77e9c91d4 | ||
|
|
4b476a6dff | ||
|
|
2057065dbf | ||
|
|
a87e5ceaf6 | ||
|
|
2eb3dc6692 | ||
|
|
5f0391fc44 | ||
|
|
1679b788c2 | ||
|
|
9a4cf388d3 | ||
|
|
f4b0f92f05 | ||
|
|
4ab4be124b | ||
|
|
99ad0c217d | ||
|
|
8ccec23d6c | ||
|
|
2a97ac66fb | ||
|
|
b26c8bba37 | ||
|
|
4b78d70b5c | ||
|
|
8450bfa611 | ||
|
|
84c6935ae4 | ||
|
|
9f52ed795d | ||
|
|
f7379c0562 | ||
|
|
51f51b54de | ||
|
|
e87418adde | ||
|
|
08ab63d704 | ||
|
|
e7cc9b92ae | ||
|
|
368c9bb814 | ||
|
|
43fd5ed660 | ||
|
|
f774d5b290 | ||
|
|
b0a7fc1648 | ||
|
|
f1193955fb | ||
|
|
2d21001e55 | ||
|
|
99118e65a6 | ||
|
|
8ff0ee6a1a | ||
|
|
ac9a27b333 | ||
|
|
84719365b1 | ||
|
|
34fb175d63 | ||
|
|
dbd8c6ab7c | ||
|
|
716765104c | ||
|
|
51a4cee688 | ||
|
|
b2f113bf0a | ||
|
|
2e02ba46e8 | ||
|
|
38666cbb8b | ||
|
|
53acae1a63 | ||
|
|
e2591061af | ||
|
|
3d52ce80f2 | ||
|
|
a598ef3ed0 | ||
|
|
e71550d557 | ||
|
|
56d621b1ff | ||
|
|
a38b3a79ce | ||
|
|
7ae60605bb | ||
|
|
7492b4bfc7 | ||
|
|
3adfec81c2 | ||
|
|
c68a3a7f07 | ||
|
|
4eaeccba1c | ||
|
|
2dea0a3b2d | ||
|
|
467f0a79c3 | ||
|
|
353a1fb6c1 | ||
|
|
8e98b778bd | ||
|
|
3ad6720f5f | ||
|
|
b0be119474 | ||
|
|
161d4f952b | ||
|
|
301252906f | ||
|
|
ddd1722f39 | ||
|
|
907afc6e4a | ||
|
|
d8da7922d9 | ||
|
|
2fc503f71f | ||
|
|
ad8e55d824 | ||
|
|
5d22722010 | ||
|
|
13cb970a55 | ||
|
|
94ebbf84cb | ||
|
|
100e940e4b | ||
|
|
a6ce6d3953 | ||
|
|
da0e99ad8c | ||
|
|
dff0267239 | ||
|
|
03e3ba6ad8 | ||
|
|
4c73315b5c | ||
|
|
71dc8dd6be | ||
|
|
f2ef160ca8 | ||
|
|
be4f6271f4 | ||
|
|
a733d81441 | ||
|
|
06cf0eebce | ||
|
|
59024eea98 | ||
|
|
62af673117 | ||
|
|
f22120dc38 | ||
|
|
1a0af863a2 | ||
|
|
0446a74d28 | ||
|
|
5562b47d84 | ||
|
|
7309ceff37 | ||
|
|
ac9abbd385 | ||
|
|
4bc5a7834e | ||
|
|
0f5fe2d4fe | ||
|
|
f5015e75e8 | ||
|
|
ddf4542b75 | ||
|
|
9dbd812b3d | ||
|
|
3d03efbe63 | ||
|
|
a522c49c1f | ||
|
|
9ec94dee15 | ||
|
|
62ed8411ae | ||
|
|
75a75365cf | ||
|
|
ac3f20c5d1 | ||
|
|
9109511e95 | ||
|
|
b63d6fccde | ||
|
|
88df2f0518 | ||
|
|
5e93bcca1e | ||
|
|
74d1eb7e28 | ||
|
|
544e30dbf8 | ||
|
|
282b3d5cca | ||
|
|
a6aa9bbc44 | ||
|
|
3140b7f998 | ||
|
|
d70362fbf1 | ||
|
|
6e8348b843 | ||
|
|
61ca7f36a4 | ||
|
|
b5e9ae5ad9 | ||
|
|
ba2d0b36af | ||
|
|
4cc47e30de | ||
|
|
d8451e56aa | ||
|
|
6abf327d0d | ||
|
|
79fb49479e | ||
|
|
2ae6ad34fc | ||
|
|
14d9618a64 | ||
|
|
05ef0ae452 | ||
|
|
883eee96a2 | ||
|
|
9c8c0533a4 | ||
|
|
14d46c007b | ||
|
|
25872c3b11 | ||
|
|
610ba8827f | ||
|
|
154b4bd627 | ||
|
|
c409a2db82 | ||
|
|
52df5bfcf4 | ||
|
|
2428853613 | ||
|
|
6fb2102099 | ||
|
|
37ab7068d9 | ||
|
|
d061aa82ea | ||
|
|
b088b84eb1 | ||
|
|
fceddc20f8 | ||
|
|
f7a6a36696 | ||
|
|
91de9b3d27 | ||
|
|
2d37f6c6e9 | ||
|
|
d75818297b | ||
|
|
faa2cb3f90 | ||
|
|
29914c09f1 | ||
|
|
7f5e5c6825 | ||
|
|
6bc4f3ec26 | ||
|
|
35622082a9 | ||
|
|
65cf97a7be | ||
|
|
cf9ed97c3e | ||
|
|
cc134464a4 | ||
|
|
e3f89e8845 | ||
|
|
c45b23fe55 | ||
|
|
b1f74c0057 | ||
|
|
bf2e8f9a7d | ||
|
|
f5f34a85ea | ||
|
|
04e5d5b7e0 | ||
|
|
3e565109b3 | ||
|
|
770a0812aa | ||
|
|
25d8faea63 | ||
|
|
98e0011e8e | ||
|
|
401a669aca | ||
|
|
4e32241d83 | ||
|
|
cfa3d69538 | ||
|
|
e01ee4575b | ||
|
|
a03185e14e | ||
|
|
564de89333 | ||
|
|
0655988ed7 | ||
|
|
c7f9090126 | ||
|
|
1bef0d4381 | ||
|
|
fcce26d4e1 | ||
|
|
6e99c7e3c5 | ||
|
|
3f83f6f09a | ||
|
|
bf90bf2295 | ||
|
|
043ba29b32 | ||
|
|
95c36b0a37 | ||
|
|
98d3a4cc56 | ||
|
|
46c8accd84 | ||
|
|
57d2f8e93d | ||
|
|
e080a54022 | ||
|
|
eff34215ee | ||
|
|
4503237ac4 | ||
|
|
bf614e1dc9 | ||
|
|
857c97006d | ||
|
|
b2478a8145 | ||
|
|
3c5ca3dca4 | ||
|
|
a92a5a0d34 | ||
|
|
c51ba1d8f6 | ||
|
|
a57833aaf8 | ||
|
|
0bcff30faa | ||
|
|
10d6d2e247 | ||
|
|
2047cb7ad8 | ||
|
|
6b793cca04 | ||
|
|
5fa9fcc57c | ||
|
|
bc617c17ef | ||
|
|
0ecd1853d0 | ||
|
|
dc2d3146a1 | ||
|
|
266aff4ffb | ||
|
|
a407e0c3fa | ||
|
|
97627dc2ff | ||
|
|
683c865eda | ||
|
|
b795a1f5dd | ||
|
|
d928c0b83d | ||
|
|
1d3567f3a3 | ||
|
|
33201dade2 | ||
|
|
5ced36b0d6 | ||
|
|
7ce954503c | ||
|
|
1c73e525ff | ||
|
|
da967f080c | ||
|
|
f0a6109a7d | ||
|
|
e84c7dcbab | ||
|
|
796ee627e7 | ||
|
|
aca4119fb1 | ||
|
|
ed95dec964 | ||
|
|
5324a41abd | ||
|
|
d453a1b291 | ||
|
|
71b915bed3 | ||
|
|
1ac4191c66 | ||
|
|
4f25500351 | ||
|
|
a0c398264c | ||
|
|
d79a577611 | ||
|
|
8d92aa91e9 | ||
|
|
5ab5576e68 | ||
|
|
c5cfe68c8a | ||
|
|
2fc241a9fa | ||
|
|
81a6b18165 | ||
|
|
084c84a277 | ||
|
|
ba9915a1df | ||
|
|
60b682b62a | ||
|
|
a3d310a3e4 | ||
|
|
bbaad041be | ||
|
|
b22ad537e2 | ||
|
|
154e3b078e | ||
|
|
a02d288df9 | ||
|
|
3314f9b94f | ||
|
|
a6afa205a9 | ||
|
|
e600c05d18 | ||
|
|
99c009542a | ||
|
|
afdf74342a | ||
|
|
552d95013b | ||
|
|
eb606bb608 | ||
|
|
f4a3add818 | ||
|
|
4462f26925 | ||
|
|
0cc76bd4f7 | ||
|
|
949a09340f | ||
|
|
5d6bed3d66 | ||
|
|
656ebd87e0 | ||
|
|
72df4288ef | ||
|
|
48dba5fa7e | ||
|
|
8a85f99eb2 | ||
|
|
2c7782c06a | ||
|
|
fb2d081402 | ||
|
|
f8f6377551 | ||
|
|
b04cbcd8c5 | ||
|
|
094df666f4 | ||
|
|
75bf8a1275 | ||
|
|
93aa7278b7 | ||
|
|
af9c916e08 | ||
|
|
01dccbfd07 | ||
|
|
ea86bd6a2f | ||
|
|
7857fb720d | ||
|
|
900d4307bb | ||
|
|
570cb8c094 | ||
|
|
828d10a8ee | ||
|
|
88908b3528 | ||
|
|
93f4a07ec6 | ||
|
|
a5f93523c1 | ||
|
|
99980c7f1d | ||
|
|
3b0941bc87 | ||
|
|
0dca7b6419 | ||
|
|
35dedfdf95 | ||
|
|
c37979801a | ||
|
|
3c93b29b86 | ||
|
|
7e2587459f | ||
|
|
621f52da2f | ||
|
|
8ab7e468a8 | ||
|
|
eb3f037a02 | ||
|
|
55032ebd7d | ||
|
|
ee5a0fb4b2 | ||
|
|
34258c6f27 | ||
|
|
47893e3b83 | ||
|
|
4c7dd74abd | ||
|
|
224e539708 | ||
|
|
ea929465a8 | ||
|
|
4b9267f375 | ||
|
|
809ef1cf60 | ||
|
|
354c137de6 | ||
|
|
574b425c02 | ||
|
|
e353fff711 | ||
|
|
4b74b8f286 | ||
|
|
339560fab2 | ||
|
|
ba7133f2e6 | ||
|
|
e8ca8cedc5 | ||
|
|
f9aa9911d5 | ||
|
|
fcd06769b2 | ||
|
|
24dcf78be8 | ||
|
|
13279299c4 | ||
|
|
20a00481d6 | ||
|
|
d55b6fb393 | ||
|
|
d63169ccc0 | ||
|
|
bb9276bec5 | ||
|
|
9aed5f751e | ||
|
|
10f564a315 | ||
|
|
c4f56c59d0 | ||
|
|
6b9d43e9ab | ||
|
|
7aacf203a0 | ||
|
|
4717e035ee | ||
|
|
f1279666b5 | ||
|
|
0e663e1dc5 | ||
|
|
ff9265b78b | ||
|
|
22aa337ac0 | ||
|
|
13350b5517 | ||
|
|
ce196abf1e | ||
|
|
3e18f6be28 | ||
|
|
deedca28a9 | ||
|
|
a1bf40bd83 | ||
|
|
d011b13d30 | ||
|
|
ef6088531a | ||
|
|
895d19a153 | ||
|
|
67c0b81067 | ||
|
|
235dc1b8db | ||
|
|
d0e5fc0cbd | ||
|
|
bd7dfd0c28 | ||
|
|
a6c400ae1c | ||
|
|
b336a4726a | ||
|
|
019e15b8bd | ||
|
|
643543002e | ||
|
|
2743788002 | ||
|
|
49280722d1 | ||
|
|
6b22b2412b | ||
|
|
14b05d1b14 | ||
|
|
88da092fdb | ||
|
|
8e25a02568 | ||
|
|
bdddc5752d | ||
|
|
d7e43d8a18 | ||
|
|
656000e7e0 | ||
|
|
ce49d9d6ad | ||
|
|
ed9af6d7a6 | ||
|
|
fd1eb1dc27 | ||
|
|
a27e4ecf61 | ||
|
|
203117d2b9 | ||
|
|
4a38c64cd6 | ||
|
|
31e1eeea50 | ||
|
|
cf41d7d1dd | ||
|
|
2bfd32a1bd | ||
|
|
5b5889dcf7 | ||
|
|
40f17bb670 | ||
|
|
871b05d4d5 | ||
|
|
2f6b07c469 | ||
|
|
fecfa9fa58 | ||
|
|
09de19b46e | ||
|
|
924f8696d0 | ||
|
|
3135314686 | ||
|
|
c83a5222ad | ||
|
|
7fe5a998c4 | ||
|
|
519aec8afe | ||
|
|
8126b51831 | ||
|
|
1309d825e4 | ||
|
|
e28215ddf3 | ||
|
|
026c292bae | ||
|
|
c2c7c74ae8 | ||
|
|
52f47a2434 | ||
|
|
f5df731491 | ||
|
|
d7ab5b912d | ||
|
|
6f737e83da | ||
|
|
18f0f55a64 | ||
|
|
4e52bb1371 | ||
|
|
e9ab141e30 | ||
|
|
ebadb8deeb | ||
|
|
63ecdcbd86 | ||
|
|
10045f6ffb | ||
|
|
bec0d44429 | ||
|
|
0a345a34a1 | ||
|
|
42ddee49a7 | ||
|
|
0c8bf9f3d0 | ||
|
|
b70aa0cb9c | ||
|
|
3aa52812ab | ||
|
|
35a46d0d07 | ||
|
|
5c84d1616c | ||
|
|
24fbeec62d | ||
|
|
a5229fae06 | ||
|
|
1195e570e4 | ||
|
|
c101b039ea | ||
|
|
444f659076 | ||
|
|
de15ed6766 | ||
|
|
d0a8e2daac | ||
|
|
f085c0eb06 | ||
|
|
cb86e78f8b | ||
|
|
10bd261a3f | ||
|
|
c337e4507d | ||
|
|
ba2d02bd3d | ||
|
|
9f644f82b1 | ||
|
|
b6797907a8 | ||
|
|
33827fea5e | ||
|
|
45d44dc74d | ||
|
|
50c3f04dcd | ||
|
|
404142cc40 | ||
|
|
ab6b93ddca | ||
|
|
2c9b23db4e | ||
|
|
8cc9b0ec66 | ||
|
|
ddee470eba | ||
|
|
bf3a9acc86 | ||
|
|
391269b1ed | ||
|
|
e44e4fa47d | ||
|
|
7fdac390de | ||
|
|
9f4a23e0f1 | ||
|
|
42e8e255c0 | ||
|
|
c345d8b7ef | ||
|
|
975d4eb8a0 | ||
|
|
ef10ebe97c | ||
|
|
81e76857da | ||
|
|
b12e0690cd | ||
|
|
3d4ba373a6 | ||
|
|
ed0c2ad45a | ||
|
|
4e406d758f | ||
|
|
18adf0852d | ||
|
|
42248bc0f4 | ||
|
|
b3f3510b2d | ||
|
|
bbf0c04f55 | ||
|
|
6af8f19bb6 | ||
|
|
95ede62e81 | ||
|
|
0c28633bbd | ||
|
|
42c8fd7bfe | ||
|
|
e129ab6a79 | ||
|
|
6e4667aa85 | ||
|
|
46a84c54ea | ||
|
|
c5e3134f37 | ||
|
|
aa0c15f6b6 | ||
|
|
23fcc2b8f7 | ||
|
|
f5f3048908 | ||
|
|
d8d9025286 | ||
|
|
66fdb550a6 | ||
|
|
e59f3cfa71 | ||
|
|
44422a5206 | ||
|
|
b67b7cf73d | ||
|
|
3ba6d4ae53 | ||
|
|
69c8eecaf1 | ||
|
|
b65b3ff940 | ||
|
|
4f06e6398f | ||
|
|
62d39841b1 | ||
|
|
2cfc4908a2 | ||
|
|
177771df6a | ||
|
|
23171f967e | ||
|
|
09f4090a4b | ||
|
|
f2db9bf2d5 | ||
|
|
0ff8470278 | ||
|
|
9b0ce7c7b7 | ||
|
|
e68b3232d9 | ||
|
|
c32ba39f43 | ||
|
|
c7371d9c3a | ||
|
|
3269cff066 | ||
|
|
75b35139e4 | ||
|
|
838b2e29cf | ||
|
|
9adc0778a9 | ||
|
|
130ce433f4 | ||
|
|
cac774d7ce | ||
|
|
7053e6be0b | ||
|
|
2743f78cd3 | ||
|
|
49f4e5bec7 | ||
|
|
228744bec8 | ||
|
|
2654197681 | ||
|
|
205d641e75 | ||
|
|
5accfeeaa7 | ||
|
|
a024dcdb61 | ||
|
|
5b661140fc | ||
|
|
ed14250fe2 | ||
|
|
21e9736c81 | ||
|
|
8097d96c72 | ||
|
|
efb686c332 | ||
|
|
bd5a8afdd5 | ||
|
|
d15134b272 | ||
|
|
b9b4af0454 | ||
|
|
18eeec0165 | ||
|
|
b2a7a6770c | ||
|
|
c40d2bb096 | ||
|
|
c7242199e7 | ||
|
|
f1fcde75c3 | ||
|
|
0f17c11c04 | ||
|
|
57390d541f | ||
|
|
e4cf91ac1d | ||
|
|
8fc0731900 | ||
|
|
ae552ede60 | ||
|
|
3dd9d6e454 | ||
|
|
7258013b88 | ||
|
|
3172759ca0 | ||
|
|
85015881d0 | ||
|
|
977d9d057f | ||
|
|
111eba8c31 | ||
|
|
8f466b4c9a | ||
|
|
057ec42b25 | ||
|
|
eab9df4490 | ||
|
|
b6549a6484 | ||
|
|
883b4487e0 | ||
|
|
699deb86c6 | ||
|
|
8fcfaec8a0 | ||
|
|
a5ea620ca8 | ||
|
|
c798b42a64 | ||
|
|
13f7fb2200 | ||
|
|
7ca9b8cece | ||
|
|
c2e51f75a1 | ||
|
|
4d9878b574 | ||
|
|
abe512c655 | ||
|
|
b67526f09b | ||
|
|
e236e87171 | ||
|
|
26fad02507 | ||
|
|
e73c327687 | ||
|
|
f3e0337584 | ||
|
|
1736ec7be2 | ||
|
|
fbc8ae25ad | ||
|
|
f92152195c | ||
|
|
bbe790e551 | ||
|
|
954f2c047c | ||
|
|
baac531655 | ||
|
|
262c72d44a | ||
|
|
5e63a524c1 | ||
|
|
5d93d5752c | ||
|
|
241efe971b | ||
|
|
8c59333a02 | ||
|
|
9a724a4bba | ||
|
|
8c8ac3bab4 | ||
|
|
843c319b1e | ||
|
|
879bc2ff8c | ||
|
|
3e303253d2 | ||
|
|
ad689ece31 | ||
|
|
1801689ef7 | ||
|
|
e77d8a709a | ||
|
|
b090b498a8 | ||
|
|
b5618412c3 | ||
|
|
bb3e07def3 | ||
|
|
a3f0f4ecae | ||
|
|
df60dd7ba0 | ||
|
|
21e7ea7d4c | ||
|
|
a091a382ca | ||
|
|
0edbffa093 | ||
|
|
08cc5dbba4 | ||
|
|
edbebd983e | ||
|
|
90eae0e944 | ||
|
|
3976056ee8 | ||
|
|
8f12c6c25f | ||
|
|
c92815d78d | ||
|
|
2fe6dcf166 | ||
|
|
33f2572979 | ||
|
|
a71dca0e70 | ||
|
|
cb37ab871b | ||
|
|
f391a3391f | ||
|
|
ce511812d5 | ||
|
|
89af33fb9d | ||
|
|
fec91e7093 | ||
|
|
a092a2fc9d | ||
|
|
775850c004 | ||
|
|
1a5dc6228c | ||
|
|
6622d508af | ||
|
|
a6a1e80afb | ||
|
|
40f885e7c4 | ||
|
|
f0fd4031bb | ||
|
|
0831723565 | ||
|
|
71a7e9dd93 | ||
|
|
07fe299ad6 | ||
|
|
b18c738f5b | ||
|
|
bcc11bda74 | ||
|
|
a108bd82b5 | ||
|
|
e3d373acf9 | ||
|
|
2a5ebe300d | ||
|
|
04e54f2794 | ||
|
|
2e75ca18f4 | ||
|
|
d8f0e92727 | ||
|
|
70c8ab537c | ||
|
|
f97677f384 | ||
|
|
bbb6d46c09 | ||
|
|
0c7cc3c0a2 | ||
|
|
0128014f51 | ||
|
|
75361de125 | ||
|
|
98be1b01f1 | ||
|
|
3ac0bf23e4 | ||
|
|
983ccac4d1 | ||
|
|
47883b8751 | ||
|
|
955b7f0c4c | ||
|
|
77bd89f8f7 | ||
|
|
135edafaee | ||
|
|
fce254f724 | ||
|
|
60607184d4 | ||
|
|
5e545c87b3 | ||
|
|
2b9705d9fc | ||
|
|
0d79531b69 | ||
|
|
b4f279d07c | ||
|
|
3cd1d67e58 | ||
|
|
2fc6c6e1a1 | ||
|
|
a63ce70d49 | ||
|
|
bc80069f18 | ||
|
|
996d99cbac | ||
|
|
a39ea0d61c | ||
|
|
3b81abed36 | ||
|
|
1b5a6a3721 | ||
|
|
b3b618b2ca | ||
|
|
7d1a35d8b2 | ||
|
|
dae05277d6 | ||
|
|
ac85786b62 | ||
|
|
325706756c | ||
|
|
ee5cbb13b0 | ||
|
|
b0e63fd3fd | ||
|
|
fd32f76bf0 | ||
|
|
49f298bc46 | ||
|
|
a3c18dcdb2 | ||
|
|
38d29a6b27 | ||
|
|
e3cac1a892 | ||
|
|
9b9070b69b | ||
|
|
84b5019dd0 | ||
|
|
ee81cd1533 | ||
|
|
5edd57a390 | ||
|
|
f261a0ea08 | ||
|
|
6182f621d6 | ||
|
|
72ed5bbe4e | ||
|
|
fac9b347c2 | ||
|
|
48ae1c2e63 | ||
|
|
d785952b7e | ||
|
|
5f13f380e9 | ||
|
|
99b93e760a | ||
|
|
11cc63adcd | ||
|
|
c057b950c8 | ||
|
|
b6f30d1ccb | ||
|
|
546b511a9a | ||
|
|
ddef087328 | ||
|
|
2355618409 | ||
|
|
28461fbe19 | ||
|
|
cdb719eb2a | ||
|
|
da692a4798 | ||
|
|
c002fc37c5 | ||
|
|
4e86f7f308 | ||
|
|
462d7bf3b5 | ||
|
|
eb2c15ba17 | ||
|
|
635b195d48 | ||
|
|
301bfa3234 | ||
|
|
4b51f363ae | ||
|
|
57485b5b4b | ||
|
|
cd7bf0fe01 | ||
|
|
595805eeaf | ||
|
|
a2c17e5e0e | ||
|
|
af2548acd1 | ||
|
|
82c576c850 | ||
|
|
0ec17645b6 | ||
|
|
b35a3e029f | ||
|
|
0037ec9606 | ||
|
|
cbd5a2786c | ||
|
|
226d7c93bd | ||
|
|
a2c82c56fb | ||
|
|
dd91c810ad | ||
|
|
fcdb2e3a28 | ||
|
|
b06c9499a3 | ||
|
|
394bf46eca | ||
|
|
0967ad12e8 | ||
|
|
fdcc060861 | ||
|
|
77384f35c4 | ||
|
|
59338a16ac | ||
|
|
a91344595c | ||
|
|
a2586424f9 | ||
|
|
d08921bde3 | ||
|
|
9214bb4f31 | ||
|
|
77aff31bcc | ||
|
|
89570857bb | ||
|
|
84d1db5f1e | ||
|
|
a87fb57188 | ||
|
|
95db6de63c | ||
|
|
bbffe1a9bd | ||
|
|
9d6b6b7c93 | ||
|
|
76e0eab94c | ||
|
|
9a13502597 | ||
|
|
77d02c7844 | ||
|
|
c3339d8ea2 | ||
|
|
7371871f15 | ||
|
|
8f0f6db9d6 | ||
|
|
27ddaf726d | ||
|
|
e6c1d928ec | ||
|
|
ae360b1db3 | ||
|
|
b5be1f3933 | ||
|
|
006344b58a | ||
|
|
b18b6af4dc | ||
|
|
4d8f489b18 | ||
|
|
76380a1f04 | ||
|
|
332593c23e | ||
|
|
fd818c5f31 | ||
|
|
1b7b163a66 | ||
|
|
1c9ca22b93 | ||
|
|
5a1a39478f | ||
|
|
983609f19a | ||
|
|
c719e37cc1 | ||
|
|
4c7f8cc6b6 | ||
|
|
1e853b040d | ||
|
|
fcc59c0674 | ||
|
|
ee091a33c9 | ||
|
|
13e7af779b | ||
|
|
a9e9dd7270 | ||
|
|
75c6abb2b1 | ||
|
|
ca7acdd812 | ||
|
|
57750689ee | ||
|
|
ecd7423ac0 | ||
|
|
f74d3e71a2 | ||
|
|
eba20025fe | ||
|
|
30da4bcd85 | ||
|
|
ab7f266e2b | ||
|
|
32f97392bb | ||
|
|
382ac94a1f | ||
|
|
42046eff3a | ||
|
|
154030c5fa | ||
|
|
38fa908872 | ||
|
|
fd5b2cc6c6 | ||
|
|
514644269c | ||
|
|
6d1b18da07 | ||
|
|
d918ed1f52 | ||
|
|
175d01f3d1 | ||
|
|
1dbc978916 | ||
|
|
14dc7ac3a3 | ||
|
|
f434bd1133 | ||
|
|
d8c2ac4708 | ||
|
|
79f68d01c8 | ||
|
|
b8a83868da | ||
|
|
0b74250248 | ||
|
|
8932378e07 | ||
|
|
1e4761a10a | ||
|
|
0a7c52c75d | ||
|
|
c61681c331 | ||
|
|
19888b3ee5 | ||
|
|
6d7f274bde | ||
|
|
706b823c4a | ||
|
|
aeb5906de3 | ||
|
|
c8d2098e5b | ||
|
|
6cb0da0ce8 | ||
|
|
d181b84c3b | ||
|
|
5732752350 | ||
|
|
0f93c4fa1b | ||
|
|
92e0e4bb91 | ||
|
|
291dc39c25 | ||
|
|
750b7571f5 | ||
|
|
87f4022759 | ||
|
|
d053e60ed2 | ||
|
|
5041930685 | ||
|
|
13307f3861 | ||
|
|
b081dc8b18 | ||
|
|
7fd5e2bc90 | ||
|
|
e973f5fd4e | ||
|
|
b2fa10f2de | ||
|
|
222ce237c5 | ||
|
|
27df34f000 | ||
|
|
50e0152ced | ||
|
|
c49d25229c | ||
|
|
28ceb7f7ff | ||
|
|
d8dd9f5738 | ||
|
|
16a1eade2a | ||
|
|
807a1bc26d | ||
|
|
e7feb83f76 | ||
|
|
a117867307 | ||
|
|
e08a6bead8 | ||
|
|
2ed1c3a9dc | ||
|
|
378d694871 | ||
|
|
8bb746aa0e | ||
|
|
c30bc69f5d | ||
|
|
73dc17318e | ||
|
|
d23118f692 | ||
|
|
b48507d92a |
281 changed files with 13269 additions and 7597 deletions
8
.babelrc
8
.babelrc
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"@babel/preset-env"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"module:babel-plugin-macros",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
9
.babelrc.json
Normal file
9
.babelrc.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"module:babel-plugin-macros",
|
||||||
|
"@babel/plugin-transform-optional-chaining"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
last 2 Chrome versions
|
last 2 Chrome versions
|
||||||
last 2 Safari versions
|
last 2 Safari versions
|
||||||
Firefox ESR
|
Firefox ESR
|
||||||
maintained node versions
|
|
||||||
not dead
|
not dead
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,31 @@
|
||||||
'use strict';
|
export default {
|
||||||
|
'F2 - Rename File': renameCurrent,
|
||||||
module.exports = {
|
'L - Lint': run('npm run lint'),
|
||||||
'F2 - Rename file': async ({DOM}) => {
|
'F - Fix Lint': run('npm run fix:lint'),
|
||||||
await DOM.renameCurrent();
|
'T - Test': run('npm run test'),
|
||||||
},
|
'C - Coverage': run('npm run coverage'),
|
||||||
'D - Build Dev': async ({CloudCmd}) => {
|
'D - Build Dev': run('npm run build:client:dev'),
|
||||||
await CloudCmd.TerminalRun.show({
|
'P - Build Prod': run('npm run build:client'),
|
||||||
command: 'npm run build:client:dev',
|
|
||||||
autoClose: false,
|
|
||||||
closeMessage: 'Press any button to close Terminal',
|
|
||||||
});
|
|
||||||
|
|
||||||
CloudCmd.refresh();
|
|
||||||
},
|
|
||||||
'P - Build Prod': async ({CloudCmd}) => {
|
|
||||||
await CloudCmd.TerminalRun.show({
|
|
||||||
command: 'npm run build:client',
|
|
||||||
autoClose: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
CloudCmd.refresh();
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function renameCurrent(DOM) {
|
||||||
|
await DOM.renameCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command) {
|
||||||
|
return async ({CloudCmd, DOM}) => {
|
||||||
|
const {TerminalRun, config} = CloudCmd;
|
||||||
|
|
||||||
|
const {CurrentInfo} = DOM;
|
||||||
|
const {dirPath} = CurrentInfo;
|
||||||
|
|
||||||
|
const cwd = config('root') + dirPath;
|
||||||
|
|
||||||
|
return await TerminalRun.show({
|
||||||
|
cwd,
|
||||||
|
command,
|
||||||
|
closeMessage: 'Press any key to close Terminal',
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,5 @@ cssnano.config.js
|
||||||
bin/release.js
|
bin/release.js
|
||||||
|
|
||||||
client
|
client
|
||||||
legacy
|
|
||||||
server_
|
server_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ root = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = false
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
|
|
||||||
34
.eslintrc.js
34
.eslintrc.js
|
|
@ -1,34 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
extends: [
|
|
||||||
'plugin:putout/recommended',
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
'putout',
|
|
||||||
'node',
|
|
||||||
],
|
|
||||||
overrides: [{
|
|
||||||
files: ['bin/release.js'],
|
|
||||||
rules: {
|
|
||||||
'no-console': 'off',
|
|
||||||
'node/shebang': 'off',
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'plugin:node/recommended',
|
|
||||||
],
|
|
||||||
}, {
|
|
||||||
files: ['bin/cloudcmd.js'],
|
|
||||||
rules: {
|
|
||||||
'no-console': 'off',
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'plugin:node/recommended',
|
|
||||||
],
|
|
||||||
}, {
|
|
||||||
files: ['{client,common}/**/*.js'],
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
github: coderaiser
|
||||||
|
open_collective: cloudcmd
|
||||||
|
ko_fi: coderaiser
|
||||||
12
.github/ISSUE_TEMPLATE.md
vendored
12
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
<!--
|
|
||||||
Thank you for reporting an issue. Please fill in the template below. If unsure
|
|
||||||
about something, just do as best as you're able.
|
|
||||||
-->
|
|
||||||
|
|
||||||
* **Version** (`cloudcmd -v`):
|
|
||||||
* **Node Version** `node -v`:
|
|
||||||
* **OS** (`uname -a` on Linux):
|
|
||||||
* **Browser name/version**:
|
|
||||||
* **Used Command Line Parameters**:
|
|
||||||
* **Changed Config**:
|
|
||||||
|
|
||||||
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: needs clarification
|
||||||
|
assignees: coderaiser
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
|
||||||
|
- **Version** (`cloudcmd -v`):
|
||||||
|
- **Node Version** `node -v`:
|
||||||
|
- **OS** (`uname -a` on Linux):
|
||||||
|
- **Browser name/version**:
|
||||||
|
- **Used Command Line Parameters**:
|
||||||
|
- **Changed Config**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
|
||||||
|
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
|
||||||
|
- [ ] 💪 **I'm willing to work on this issue**
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Stack Overflow
|
||||||
|
url: https://stackoverflow.com/search?q=cloudcmd
|
||||||
|
about: Please ask and answer questions here.
|
||||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
24
.github/ISSUE_TEMPLATE/issue_template.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/issue_template.md
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
***
|
||||||
|
|
||||||
|
name: Tracking issue
|
||||||
|
about: Create an issue with bug report or feature request.
|
||||||
|
title: ""
|
||||||
|
labels: needs triage
|
||||||
|
assignees: coderaiser
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
- **Version** (`cloudcmd -v`):
|
||||||
|
- **Node Version** `node -v`:
|
||||||
|
- **OS** (`uname -a` on Linux):
|
||||||
|
- **Browser name/version**:
|
||||||
|
- **Used Command Line Parameters**:
|
||||||
|
- **Changed Config**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
|
||||||
|
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
|
||||||
|
- [ ] 💪 **I'm willing to work on this issue**
|
||||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -4,6 +4,5 @@ about something, just do as best as you're able.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide")
|
- [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide")
|
||||||
- [ ] `npm run codestyle` is OK
|
- [ ] `npm run fix:lint` is OK
|
||||||
- [ ] `npm test` is OK
|
- [ ] `npm test` is OK
|
||||||
|
|
||||||
|
|
|
||||||
60
.github/workflows/docker-io.yml
vendored
Normal file
60
.github/workflows/docker-io.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Docker IO
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
jobs:
|
||||||
|
buildx:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
- name: Use Node.js 24.x
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24.x
|
||||||
|
- name: Install Redrun
|
||||||
|
run: bun i redrun -g --no-save
|
||||||
|
- name: NPM Install
|
||||||
|
run: bun i --no-save
|
||||||
|
- name: Lint
|
||||||
|
run: redrun lint
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
redrun build
|
||||||
|
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v4
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v4
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push io-image
|
||||||
|
uses: docker/build-push-action@v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile.io
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
coderaiser/cloudcmd:io
|
||||||
|
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-io
|
||||||
|
ghcr.io/${{ github.repository }}-io
|
||||||
|
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-io
|
||||||
88
.github/workflows/docker.yml
vendored
Normal file
88
.github/workflows/docker.yml
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
name: Docker CI
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "*"
|
||||||
|
jobs:
|
||||||
|
buildx:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
- name: Use Node.js 22.x
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22.x
|
||||||
|
- name: Install Redrun
|
||||||
|
run: bun i redrun -g --no-save
|
||||||
|
- name: NPM Install
|
||||||
|
run: bun i --no-save
|
||||||
|
- name: Lint
|
||||||
|
run: redrun lint
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
redrun build
|
||||||
|
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v4
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v4
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push base-image
|
||||||
|
uses: docker/build-push-action@v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
coderaiser/cloudcmd:latest
|
||||||
|
coderaiser/cloudcmd:${{ steps.build.outputs.version }}
|
||||||
|
ghcr.io/${{ github.repository }}:latest
|
||||||
|
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}
|
||||||
|
- name: Build and push alpine-image
|
||||||
|
uses: docker/build-push-action@v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile.alpine
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
coderaiser/cloudcmd:alpine
|
||||||
|
coderaiser/cloudcmd:latest-alpine
|
||||||
|
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine
|
||||||
|
ghcr.io/${{ github.repository }}:latest-alpine
|
||||||
|
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-alpine
|
||||||
|
- name: Build and push slim-image
|
||||||
|
uses: docker/build-push-action@v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile.slim
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
coderaiser/cloudcmd:slim
|
||||||
|
coderaiser/cloudcmd:latest-slim
|
||||||
|
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-slim
|
||||||
|
ghcr.io/${{ github.repository }}-slim
|
||||||
|
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-slim
|
||||||
61
.github/workflows/nodejs.yml
vendored
61
.github/workflows/nodejs.yml
vendored
|
|
@ -1,20 +1,53 @@
|
||||||
name: Node CI
|
name: Node CI
|
||||||
|
on:
|
||||||
on: [push]
|
- push
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
NAME: cloudcmd
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version:
|
||||||
|
- 22.x
|
||||||
|
- 24.x
|
||||||
|
- 26.x
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v5
|
||||||
- name: Use Node.js 12.x
|
- uses: oven-sh/setup-bun@v2
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
with:
|
||||||
version: 12.x
|
bun-version: latest
|
||||||
- name: npm install, build, and test
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
run: |
|
uses: actions/setup-node@v6
|
||||||
npm install
|
with:
|
||||||
npm run build
|
node-version: ${{ matrix.node-version }}
|
||||||
npm test
|
- name: Install Redrun
|
||||||
|
run: bun i redrun madrun -g --no-save
|
||||||
|
- name: Install
|
||||||
|
run: bun i --no-save
|
||||||
|
- name: Init Madrun
|
||||||
|
run: madrun --init
|
||||||
|
- name: Lint
|
||||||
|
run: redrun fix:lint
|
||||||
|
- name: Typos
|
||||||
|
uses: coderaiser/typos.ai@v1.1.8
|
||||||
|
with:
|
||||||
|
key: ${{ secrets.TYPOS_AI_KEY }}
|
||||||
|
- name: Commit fixes
|
||||||
|
uses: EndBug/add-and-commit@v10
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
message: "chore: ${{ env.NAME }}: actions: lint ☘️"
|
||||||
|
- name: Build
|
||||||
|
run: redrun build
|
||||||
|
- name: Test
|
||||||
|
run: redrun test
|
||||||
|
- name: Coverage
|
||||||
|
run: redrun coverage coverage:report
|
||||||
|
- name: Coveralls
|
||||||
|
uses: coverallsapp/github-action@v2
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
||||||
23
.gitignore
vendored
23
.gitignore
vendored
|
|
@ -1,18 +1,17 @@
|
||||||
package-lock.json
|
*.swp
|
||||||
yarn.lock
|
*.log
|
||||||
yarn-error.log
|
*.lock
|
||||||
node_modules
|
|
||||||
npm-debug.log*
|
|
||||||
coverage
|
|
||||||
|
|
||||||
modules/execon
|
|
||||||
modules/emitify
|
|
||||||
|
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
|
||||||
*.swp
|
package-lock.json
|
||||||
.putoutcache
|
npm-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
coverage
|
||||||
|
modules/execon
|
||||||
|
modules/emitify
|
||||||
dist
|
dist
|
||||||
dist-dev
|
dist-dev
|
||||||
|
|
||||||
|
|
|
||||||
204
.madrun.js
204
.madrun.js
|
|
@ -1,178 +1,62 @@
|
||||||
'use strict';
|
import {run, cutEnv} from 'madrun';
|
||||||
|
import {defineEnv} from 'supertape/env';
|
||||||
|
|
||||||
const {
|
const testEnv = defineEnv({
|
||||||
run,
|
timeout: 7000,
|
||||||
parallel,
|
css: true,
|
||||||
predefined,
|
});
|
||||||
} = require('madrun');
|
|
||||||
|
|
||||||
const {version} = require('./package');
|
const buildEnv = {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
};
|
||||||
|
|
||||||
const names = [
|
export default {
|
||||||
'bin/cloudcmd.js',
|
|
||||||
'client',
|
|
||||||
'common',
|
|
||||||
'server',
|
|
||||||
'test',
|
|
||||||
'bin/release.js',
|
|
||||||
'webpack.config.js',
|
|
||||||
'cssnano.config.js',
|
|
||||||
'.webpack',
|
|
||||||
'.eslintrc.js',
|
|
||||||
'.madrun.js',
|
|
||||||
'{client,server,common}/**/*.spec.js',
|
|
||||||
];
|
|
||||||
|
|
||||||
const {putout} = predefined;
|
|
||||||
|
|
||||||
const env = 'THREAD_IT_COUNT=0';
|
|
||||||
const dockerName = 'coderaiser/cloudcmd';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
'start': () => 'node bin/cloudcmd.js',
|
'start': () => 'node bin/cloudcmd.js',
|
||||||
'start:dev': () => `NODE_ENV=development ${run('start')}`,
|
'start:dev': async () => await run('start', null, {
|
||||||
'build:start': () => run(['build:client', 'start']),
|
NODE_ENV: 'development',
|
||||||
'build:start:dev': () => run(['build:client:dev', 'start:dev']),
|
|
||||||
'lint:all': () => run(['lint', 'lint:css', 'spell']),
|
|
||||||
'lint:base': () => putout({
|
|
||||||
names,
|
|
||||||
}),
|
}),
|
||||||
'lint:css': () => 'stylelint css/*.css',
|
'build:start': () => run(['build:client', 'start']),
|
||||||
'spell': () => 'yaspeller .',
|
'build:start:dev': () => run([
|
||||||
'fix:lint': () => run(['lint', 'lint:css'], '--fix'),
|
'build:client:dev',
|
||||||
'lint': () => run('lint:progress', '--cache'),
|
'start:dev',
|
||||||
'lint:progress': () => run('lint:base', '-f progress'),
|
]),
|
||||||
'lint:stream': () => run('lint:base', '-f stream'),
|
'lint:all': () => run('lint:progress'),
|
||||||
|
'lint': () => 'redlint fix; putout .',
|
||||||
'test:base': () => {
|
'lint:progress': () => run('lint', '-f progress'),
|
||||||
const cmd = 'tape';
|
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
|
||||||
const names = `'test/**/*.js' '{client,static,common,server}/**/*.spec.js'`;
|
'fresh:lint': () => run('lint', '--fresh'),
|
||||||
|
'lint:fresh': () => run('lint', '--fresh'),
|
||||||
return `${cmd} ${names}`;
|
'fix:lint': async () => `putout --fix . && redlint fix`,
|
||||||
},
|
'lint:stream': () => run('lint', '-f stream'),
|
||||||
|
'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`],
|
||||||
'test': () => `${env} ${run('test:base')}`,
|
'test:e2e': () => `tape 'test-e2e/**/*.js'`,
|
||||||
'test:client': () => `tape 'test/client/**/*.js'`,
|
'test:client': () => `tape 'test/client/**/*.js'`,
|
||||||
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
||||||
'wisdom': () => run(['lint:all', 'build', 'test']),
|
'wisdom': async () => await run(['lint:all', 'build', 'test'], null, {
|
||||||
|
CI: 1,
|
||||||
|
}),
|
||||||
'wisdom:type': () => 'bin/release.js',
|
'wisdom:type': () => 'bin/release.js',
|
||||||
'docker:pull': () => 'docker pull node',
|
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
|
||||||
'docker:pull:alpine': () => 'docker pull mhart/alpine-node',
|
'coverage:report': () => 'c8 report --reporter=lcov',
|
||||||
'docker:pull:arm32': () => 'docker pull arm32v7/node:slim',
|
'report': () => 'c8 report --reporter=lcov',
|
||||||
'docker:pull:arm64': () => 'docker pull arm64v8/node:slim',
|
'6to5': () => [buildEnv, 'rspack build --config rspack.config.js'],
|
||||||
'docker:push': () => dockerPush('x64', version),
|
|
||||||
'docker:push:latest': () => dockerPush('x64'),
|
|
||||||
'docker:push:alpine': () => dockerPush('alpine', version),
|
|
||||||
'docker:push:alpine:latest': () => dockerPush('alpine'),
|
|
||||||
'docker:push:arm32': () => dockerPush('arm32', version),
|
|
||||||
'docker:push:arm32:latest': () => dockerPush('arm32'),
|
|
||||||
'docker:push:arm64': () => dockerPush('arm64', version),
|
|
||||||
'docker:push:arm64:latest': () => dockerPush('arm64'),
|
|
||||||
'docker:build': () => dockerBuild('docker/Dockerfile', 'x64', version),
|
|
||||||
'docker:build:alpine': () => dockerBuild('docker/Dockerfile.alpine', 'alpine', version),
|
|
||||||
'docker:build:arm32': () => dockerBuild('docker/arm/Dockerfile.arm32v7', 'arm32', version),
|
|
||||||
'docker:build:arm64': () => dockerBuild('docker/arm/Dockerfile.arm64v8', 'arm64', version),
|
|
||||||
'docker:manifest:create': () => {
|
|
||||||
const images = [
|
|
||||||
`${dockerName}:latest`,
|
|
||||||
`${dockerName}:latest-x64`,
|
|
||||||
// `${dockerName}:latest-arm32`,
|
|
||||||
// `${dockerName}:latest-arm64`,
|
|
||||||
].join(' ');
|
|
||||||
|
|
||||||
return `docker manifest create ${images}`;
|
|
||||||
},
|
|
||||||
'docker:manifest:push': () => `docker manifest push ${dockerName}:latest`,
|
|
||||||
'docker': () => run(['docker:x64', 'docker:alpine', 'docker:manifest:*']),
|
|
||||||
'docker-ci': () => run(['build', 'docker-login', 'docker']),
|
|
||||||
'docker-login': () => 'docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD',
|
|
||||||
|
|
||||||
'docker:x64': () => run([
|
|
||||||
'docker:pull',
|
|
||||||
'docker:build',
|
|
||||||
'docker:tag',
|
|
||||||
'docker:push',
|
|
||||||
'docker:push:latest',
|
|
||||||
]),
|
|
||||||
|
|
||||||
'docker:alpine': () => run([
|
|
||||||
'docker:pull:alpine',
|
|
||||||
'docker:build:alpine',
|
|
||||||
'docker:tag:alpine',
|
|
||||||
'docker:push:alpine',
|
|
||||||
'docker:push:alpine:latest',
|
|
||||||
]),
|
|
||||||
|
|
||||||
/*
|
|
||||||
'docker:arm32': () => run([
|
|
||||||
'docker:pull:arm32',
|
|
||||||
'docker:build:arm32',
|
|
||||||
'docker:tag:arm32',
|
|
||||||
'docker:push:arm32',
|
|
||||||
'docker:push:arm32:latest',
|
|
||||||
]),
|
|
||||||
|
|
||||||
'docker:arm64': () => run([
|
|
||||||
'docker:pull:arm64',
|
|
||||||
'docker:build:arm64',
|
|
||||||
'docker:tag:arm64',
|
|
||||||
'docker:push:arm64',
|
|
||||||
'docker:push:arm64:latest',
|
|
||||||
]),
|
|
||||||
*/
|
|
||||||
|
|
||||||
'docker:manifest': () => run([
|
|
||||||
'docker:manifest:create',
|
|
||||||
'docker:manifest:push',
|
|
||||||
]),
|
|
||||||
|
|
||||||
'docker:tag': () => dockerTag('x64', version),
|
|
||||||
'docker:tag:alpine': () => dockerTag('alpine', version),
|
|
||||||
'docker:tag:arm32': () => dockerTag('arm32', version),
|
|
||||||
'docker:tag:arm64': () => dockerTag('arm64', version),
|
|
||||||
'docker:rm:version': () => dockerRmi('x64', version),
|
|
||||||
'docker:rm:latest': () => dockerRmi('x64'),
|
|
||||||
'docker:rm:alpine': () => dockerRmi('alpine', version),
|
|
||||||
'docker:rm:latest-alpine': () => dockerRmi('alpine'),
|
|
||||||
'docker:rm:arm32': () => dockerRmi('arm32', version),
|
|
||||||
'docker:rm:latest-arm32': () => dockerRmi('arm32'),
|
|
||||||
'docker:rm:arm64': () => dockerRmi('arm64', version),
|
|
||||||
'docker:rm:latest-arm64': () => dockerRmi('arm64'),
|
|
||||||
'docker:rm-old': () => `${parallel('docker:rm:*')} || true`,
|
|
||||||
|
|
||||||
'coverage': () => `${env} nyc ${run('test:base')}`,
|
|
||||||
'report': () => 'nyc report --reporter=text-lcov | coveralls',
|
|
||||||
'6to5': () => 'webpack --progress',
|
|
||||||
'6to5:client': () => run('6to5', '--mode production'),
|
'6to5:client': () => run('6to5', '--mode production'),
|
||||||
'6to5:client:dev': () => `NODE_ENV=development ${run('6to5', '--mode development')}`,
|
'6to5:client:dev': async () => await run('6to5', '--mode development', {
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
}),
|
||||||
'pre6to5:client': () => 'rimraf dist',
|
'pre6to5:client': () => 'rimraf dist',
|
||||||
'pre6to5:client:dev': () => 'rimraf dist-dev',
|
'pre6to5:client:dev': () => 'rimraf dist-dev',
|
||||||
'watch:client': () => run('6to5:client', '--watch'),
|
'watch:client': () => run('6to5:client', '--watch'),
|
||||||
'watch:client:dev': () => run('6to5:client:dev', '--watch'),
|
'watch:client:dev': () => run('6to5:client:dev', '--watch'),
|
||||||
'watch:server': () => 'nodemon bin/cloudcmd.js',
|
'watch:server': () => 'nodemon bin/cloudcmd.js',
|
||||||
'watch:lint': () => `nodemon -w client -w server -w webpack.config.js -x ${run('lint')}`,
|
'watch:test': async () => [testEnv, `nodemon -w client -w server -w test -w common -x ${await cutEnv('test')}`],
|
||||||
'watch:test': () => `nodemon -w client -w server -w test -w common -x ${run('test')}`,
|
'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`,
|
||||||
'watch:test:client': () => `nodemon -w client -w test/client -x ${run('test:client')}`,
|
'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`,
|
||||||
'watch:test:server': () => `nodemon -w client -w test/client -x ${run('test:server')}`,
|
'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`],
|
||||||
'watch:coverage': () => `nodemon -w server -w test -w common -x ${run('coverage')}`,
|
'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`,
|
||||||
'build': () => run('6to5:*'),
|
'build': async () => run('6to5:*'),
|
||||||
|
'build:dev': async () => run('build:client:dev'),
|
||||||
'build:client': () => run('6to5:client'),
|
'build:client': () => run('6to5:client'),
|
||||||
'build:client:dev': () => run('6to5:client:dev'),
|
'build:client:dev': () => run('6to5:client:dev'),
|
||||||
'heroku-postbuild': () => run('6to5:client'),
|
'heroku-postbuild': () => run('6to5:client'),
|
||||||
};
|
};
|
||||||
|
|
||||||
function dockerPush(type, version = 'latest') {
|
|
||||||
return `docker push coderaiser/cloudcmd:${version}-${type}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dockerBuild(file, type, version) {
|
|
||||||
return `docker build -f ${file} -t coderaiser/cloudcmd:${version}-${type} .`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dockerTag(type, version) {
|
|
||||||
return `docker tag coderaiser/cloudcmd:${version}-${type} coderaiser/cloudcmd:latest-${type}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dockerRmi(type, version = 'latest') {
|
|
||||||
return `docker rmi -f coderaiser/cloudcmd:${version}-${type}`;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
49
.npmignore
49
.npmignore
|
|
@ -1,30 +1,29 @@
|
||||||
.*
|
*.spec.*
|
||||||
*.spec.js
|
*.config.*
|
||||||
*.fixture.js*
|
*.fixture.js*
|
||||||
manifest.yml
|
|
||||||
docker
|
|
||||||
docker-compose.yml
|
|
||||||
test
|
|
||||||
fixture
|
|
||||||
coverage
|
|
||||||
css
|
|
||||||
html
|
|
||||||
yarn-error.log
|
|
||||||
yarn.lock
|
|
||||||
now.json
|
|
||||||
cssnano.config.js
|
|
||||||
|
|
||||||
app.json
|
|
||||||
bower.json
|
|
||||||
manifest.yml
|
|
||||||
|
|
||||||
bin/release.js
|
|
||||||
|
|
||||||
client
|
|
||||||
|
|
||||||
webpack.config.js
|
|
||||||
|
|
||||||
*.ai
|
*.ai
|
||||||
*.cdr
|
*.cdr
|
||||||
*.eps
|
*.eps
|
||||||
|
*.log
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
.*
|
||||||
|
|
||||||
|
manifest.yml
|
||||||
|
docker-compose.yml
|
||||||
|
now.json
|
||||||
|
app.json
|
||||||
|
bower.json
|
||||||
|
deno.json
|
||||||
|
bin/release.*
|
||||||
|
img/logo/cloudcmd-hq.png
|
||||||
|
webpack.config.js
|
||||||
|
|
||||||
|
docker
|
||||||
|
test*
|
||||||
|
fixture
|
||||||
|
fixture-*
|
||||||
|
coverage
|
||||||
|
css
|
||||||
|
html
|
||||||
|
client
|
||||||
|
|
|
||||||
15
.nycrc.json
Normal file
15
.nycrc.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"checkCoverage": false,
|
||||||
|
"all": false,
|
||||||
|
"exclude": [
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.*.js",
|
||||||
|
"**/*.config.*",
|
||||||
|
"**/fixture",
|
||||||
|
"**/test/**"
|
||||||
|
],
|
||||||
|
"branches": 100,
|
||||||
|
"lines": 100,
|
||||||
|
"functions": 100,
|
||||||
|
"statements": 100
|
||||||
|
}
|
||||||
73
.putout.json
73
.putout.json
|
|
@ -1,27 +1,60 @@
|
||||||
{
|
{
|
||||||
"match": {
|
"plugins": ["cloudcmd"],
|
||||||
"server": {
|
|
||||||
"remove-process-exit": true
|
|
||||||
},
|
|
||||||
"server/(server|exit).js": {
|
|
||||||
"remove-process-exit": false,
|
|
||||||
"remove-console": false
|
|
||||||
},
|
|
||||||
"server/(terminal|distribute/log).js": {
|
|
||||||
"remove-console": false
|
|
||||||
},
|
|
||||||
"client/(client|cloudcmd|load-module).js": {
|
|
||||||
"remove-console": false
|
|
||||||
},
|
|
||||||
"client/modules/config/index.js": {
|
|
||||||
"apply-shorthand-properties": [{
|
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"ONE_MINUTE"
|
"*.md",
|
||||||
]
|
"app.json",
|
||||||
}]
|
"fontello.json",
|
||||||
|
"html",
|
||||||
|
"fixture*"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"package-json/add-type": "off"
|
||||||
|
},
|
||||||
|
"match": {
|
||||||
|
".filesystem.json": {
|
||||||
|
"nodejs/rename-file-cjs-to-js": "off"
|
||||||
|
},
|
||||||
|
"base64": {
|
||||||
|
"types/convert-typeof-to-is-type": "off"
|
||||||
|
},
|
||||||
|
"*.md": {
|
||||||
|
"nodejs/convert-commonjs-to-esm": "on"
|
||||||
|
},
|
||||||
|
".webpack": {
|
||||||
|
"webpack": "on"
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"nodejs/remove-process-exit": "on"
|
||||||
|
},
|
||||||
|
"server/{server,exit}.js": {
|
||||||
|
"nodejs/remove-process-exit": "off"
|
||||||
|
},
|
||||||
|
"server/{server,exit,terminal,distribute/log}.{js,mjs}": {
|
||||||
|
"remove-console": "off"
|
||||||
|
},
|
||||||
|
"client/{client,cloudcmd,load-module}.{js,mjs}": {
|
||||||
|
"remove-console": "off"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"nodejs": "off"
|
||||||
|
},
|
||||||
|
"client/sw": {
|
||||||
|
"remove-console": "off"
|
||||||
},
|
},
|
||||||
"test/common/cloudfunc.js": {
|
"test/common/cloudfunc.js": {
|
||||||
"remove-console": false
|
"remove-console": "off"
|
||||||
|
},
|
||||||
|
"storage.js": {
|
||||||
|
"promises/remove-useless-async": "off"
|
||||||
|
},
|
||||||
|
"docker.yml": {
|
||||||
|
"github/set-node-versions": "off"
|
||||||
|
},
|
||||||
|
"vim.js": {
|
||||||
|
"merge-duplicate-functions": "off"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"nodejs/declare": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
.rspack/css.js
Normal file
38
.rspack/css.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {env} from 'node:process';
|
||||||
|
import {rspack} from '@rspack/core';
|
||||||
|
|
||||||
|
const {CssExtractRspackPlugin} = rspack;
|
||||||
|
const isDev = env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new CssExtractRspackPlugin({
|
||||||
|
filename: '[name].css',
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const rules = [{
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: [CssExtractRspackPlugin.loader, {
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
||||||
|
type: 'asset/inline',
|
||||||
|
}];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mode: isDev ? 'development' : 'production',
|
||||||
|
plugins,
|
||||||
|
module: {
|
||||||
|
rules,
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: !isDev,
|
||||||
|
minimizer: [
|
||||||
|
new rspack.LightningCssMinimizerRspackPlugin(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
36
.rspack/html.js
Normal file
36
.rspack/html.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import {env} from 'node:process';
|
||||||
|
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||||
|
|
||||||
|
const isDev = env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
export const plugins = [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
inject: false,
|
||||||
|
template: 'html/index.html',
|
||||||
|
minify: !isDev && getMinifyHtmlOptions(),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
function getMinifyHtmlOptions() {
|
||||||
|
return {
|
||||||
|
removeComments: true,
|
||||||
|
removeCommentsFromCDATA: true,
|
||||||
|
removeCDATASectionsFromCDATA: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
/* оставляем, поскольку у нас
|
||||||
|
* в элемент fm генерируеться
|
||||||
|
* таблица файлов
|
||||||
|
*/
|
||||||
|
removeEmptyElements: false,
|
||||||
|
removeOptionalTags: true,
|
||||||
|
removeScriptTypeAttributes: true,
|
||||||
|
removeStyleLinkTypeAttributes: true,
|
||||||
|
|
||||||
|
minifyJS: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
173
.rspack/js.js
Normal file
173
.rspack/js.js
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
import {resolve, sep} from 'node:path';
|
||||||
|
import {fileURLToPath} from 'node:url';
|
||||||
|
import {env} from 'node:process';
|
||||||
|
import {rspack} from '@rspack/core';
|
||||||
|
|
||||||
|
const resolveModule = (a) => fileURLToPath(import.meta.resolve(a));
|
||||||
|
|
||||||
|
const {
|
||||||
|
EnvironmentPlugin,
|
||||||
|
NormalModuleReplacementPlugin,
|
||||||
|
ProvidePlugin,
|
||||||
|
} = rspack;
|
||||||
|
|
||||||
|
const modules = './modules';
|
||||||
|
const dirModules = './client/modules';
|
||||||
|
const dirCss = './css';
|
||||||
|
const dirThemes = `${dirCss}/themes`;
|
||||||
|
const dirColumns = `${dirCss}/columns`;
|
||||||
|
const dir = './client';
|
||||||
|
const {NODE_ENV} = env;
|
||||||
|
const isDev = NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const rootDir = new URL('..', import.meta.url).pathname;
|
||||||
|
const dist = resolve(rootDir, 'dist');
|
||||||
|
const distDev = resolve(rootDir, 'dist-dev');
|
||||||
|
const devtool = isDev ? 'eval' : 'source-map';
|
||||||
|
|
||||||
|
const noParse = (a) => a.endsWith('.spec.js');
|
||||||
|
|
||||||
|
// codegen.macro is a babel-macro (build-time codegen), not supported by
|
||||||
|
// Rspack's native SWC transform, so client/sw/sw.js (the only file that
|
||||||
|
// uses it) keeps going through babel-loader. Everything else uses
|
||||||
|
// Rspack's built-in SWC loader, which is the main source of the speedup.
|
||||||
|
const rules = [{
|
||||||
|
test: /sw\/sw\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
}, {
|
||||||
|
test: /\.[mc]?js$/,
|
||||||
|
exclude: [/node_modules/, /sw\/sw\.js$/],
|
||||||
|
loader: 'builtin:swc-loader',
|
||||||
|
options: {
|
||||||
|
jsc: {
|
||||||
|
parser: {
|
||||||
|
syntax: 'ecmascript',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
targets: 'defaults',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
|
||||||
|
resource.request = resource.request.replace(/^node:/, '');
|
||||||
|
}),
|
||||||
|
new NormalModuleReplacementPlugin(/^putout$/, '@putout/bundle'),
|
||||||
|
new EnvironmentPlugin({
|
||||||
|
NODE_ENV,
|
||||||
|
}),
|
||||||
|
new ProvidePlugin({
|
||||||
|
process: 'process/browser',
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const splitChunks = {
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
abcCommon: {
|
||||||
|
name: 'cloudcmd.common',
|
||||||
|
chunks: (chunk) => {
|
||||||
|
const lazyChunks = [
|
||||||
|
'sw',
|
||||||
|
'nojs',
|
||||||
|
'view',
|
||||||
|
'edit',
|
||||||
|
'terminal',
|
||||||
|
'config',
|
||||||
|
'user-menu',
|
||||||
|
'help',
|
||||||
|
'themes/dark',
|
||||||
|
'themes/light',
|
||||||
|
'columns/name-size',
|
||||||
|
'columns/name-size-date',
|
||||||
|
'columns/name-size-time',
|
||||||
|
'columns/name-size-date-time',
|
||||||
|
];
|
||||||
|
|
||||||
|
return !lazyChunks.includes(chunk.name);
|
||||||
|
},
|
||||||
|
minChunks: 1,
|
||||||
|
enforce: true,
|
||||||
|
priority: -1,
|
||||||
|
reuseExistingChunk: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
resolve: {
|
||||||
|
symlinks: false,
|
||||||
|
alias: {
|
||||||
|
'node:process': 'process',
|
||||||
|
'node:path': 'path',
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
path: resolveModule('path-browserify'),
|
||||||
|
process: resolveModule('process/browser'),
|
||||||
|
util: resolveModule('util'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devtool,
|
||||||
|
optimization: {
|
||||||
|
splitChunks,
|
||||||
|
},
|
||||||
|
entry: {
|
||||||
|
'themes/dark': `${dirThemes}/dark.css`,
|
||||||
|
'themes/light': `${dirThemes}/light.css`,
|
||||||
|
'columns/name-size': `${dirColumns}/name-size.css`,
|
||||||
|
'columns/name-size-date': `${dirColumns}/name-size-date.css`,
|
||||||
|
'columns/name-size-date-time': `${dirColumns}/name-size-date-time.css`,
|
||||||
|
'nojs': `${dirCss}/nojs.css`,
|
||||||
|
'help': `${dirCss}/help.css`,
|
||||||
|
'view': `${dirCss}/view.css`,
|
||||||
|
'config': `${dirCss}/config.css`,
|
||||||
|
'terminal': `${dirCss}/terminal.css`,
|
||||||
|
'user-menu': `${dirCss}/user-menu.css`,
|
||||||
|
'sw': `${dir}/sw/sw.js`,
|
||||||
|
'cloudcmd': `${dir}/cloudcmd.js`,
|
||||||
|
[`${modules}/edit`]: `${dirModules}/edit.js`,
|
||||||
|
[`${modules}/edit-file`]: `${dirModules}/edit-file.js`,
|
||||||
|
[`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`,
|
||||||
|
[`${modules}/edit-names`]: `${dirModules}/edit-names.js`,
|
||||||
|
[`${modules}/edit-names-vim`]: `${dirModules}/edit-names-vim.js`,
|
||||||
|
[`${modules}/menu`]: `${dirModules}/menu/index.js`,
|
||||||
|
[`${modules}/view`]: `${dirModules}/view/index.js`,
|
||||||
|
[`${modules}/help`]: `${dirModules}/help.js`,
|
||||||
|
[`${modules}/markdown`]: `${dirModules}/markdown.js`,
|
||||||
|
[`${modules}/config`]: `${dirModules}/config/index.js`,
|
||||||
|
[`${modules}/contact`]: `${dirModules}/contact.js`,
|
||||||
|
[`${modules}/upload`]: `${dirModules}/upload.js`,
|
||||||
|
[`${modules}/operation`]: `${dirModules}/operation/index.js`,
|
||||||
|
[`${modules}/konsole`]: `${dirModules}/konsole.js`,
|
||||||
|
[`${modules}/terminal`]: `${dirModules}/terminal.js`,
|
||||||
|
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,
|
||||||
|
[`${modules}/cloud`]: `${dirModules}/cloud.js`,
|
||||||
|
[`${modules}/user-menu`]: `${dirModules}/user-menu/index.js`,
|
||||||
|
[`${modules}/polyfill`]: `${dirModules}/polyfill.js`,
|
||||||
|
[`${modules}/command-line`]: `${dirModules}/command-line.js`,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
path: isDev ? distDev : dist,
|
||||||
|
pathinfo: isDev,
|
||||||
|
devtoolModuleFilenameTemplate,
|
||||||
|
publicPath: '/dist/',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules,
|
||||||
|
noParse,
|
||||||
|
},
|
||||||
|
plugins,
|
||||||
|
performance: {
|
||||||
|
maxEntrypointSize: 800_000,
|
||||||
|
maxAssetSize: 600_000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function devtoolModuleFilenameTemplate(info) {
|
||||||
|
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
|
||||||
|
return `file://cloudcmd/${resource}`;
|
||||||
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
extends: stylelint-config-standard
|
|
||||||
rules:
|
|
||||||
indentation: 4
|
|
||||||
declaration-block-trailing-semicolon: always
|
|
||||||
declaration-colon-space-before: null
|
|
||||||
selector-list-comma-newline-after: null
|
|
||||||
comment-empty-line-before: null
|
|
||||||
number-leading-zero: null
|
|
||||||
number-no-trailing-zeros: null
|
|
||||||
string-quotes: single
|
|
||||||
function-url-quotes: never
|
|
||||||
no-eol-whitespace: null
|
|
||||||
font-family-name-quotes: always-unless-keyword
|
|
||||||
font-family-no-missing-generic-family-keyword: null
|
|
||||||
rule-empty-line-before: null
|
|
||||||
max-empty-lines: 2
|
|
||||||
|
|
||||||
52
.travis.yml
52
.travis.yml
|
|
@ -1,52 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 14
|
|
||||||
- 12
|
|
||||||
- 10
|
|
||||||
|
|
||||||
cache:
|
|
||||||
npm: false
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- os: windows
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
- windows
|
|
||||||
|
|
||||||
# https://docs.travis-ci.com/user/customizing-the-build/#git-end-of-line-conversion-control
|
|
||||||
# need for windows
|
|
||||||
git:
|
|
||||||
autocrlf: input
|
|
||||||
|
|
||||||
|
|
||||||
script:
|
|
||||||
- npm run lint && npm run build && npm run coverage && npm run report
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
on_success: never
|
|
||||||
on_failure: change
|
|
||||||
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
before_deploy:
|
|
||||||
- echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json
|
|
||||||
- mkdir -p $HOME/.docker
|
|
||||||
- echo '{"experimental":"enabled"}' | tee $HOME/.docker/config.json
|
|
||||||
- sudo service docker start
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
provider: script
|
|
||||||
script: npm run docker-ci
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
node: 12
|
|
||||||
condition: $TRAVIS_OS_NAME = linux
|
|
||||||
tags: true
|
|
||||||
all_branches: false
|
|
||||||
2
.typos.toml
Normal file
2
.typos.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[files]
|
||||||
|
extend-exclude = ["ChangeLog"]
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const {
|
|
||||||
basename,
|
|
||||||
extname,
|
|
||||||
join,
|
|
||||||
} = require('path');
|
|
||||||
|
|
||||||
const {env} = process;
|
|
||||||
const isDev = env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
|
||||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
|
||||||
|
|
||||||
const extractCSS = (a) => new ExtractTextPlugin(`${a}.css`);
|
|
||||||
const extractMain = extractCSS('[name]');
|
|
||||||
|
|
||||||
const cssNames = [
|
|
||||||
'nojs',
|
|
||||||
'view',
|
|
||||||
'config',
|
|
||||||
'terminal',
|
|
||||||
'user-menu',
|
|
||||||
...getCSSList('columns'),
|
|
||||||
];
|
|
||||||
|
|
||||||
const cssPlugins = cssNames.map(extractCSS);
|
|
||||||
const clean = (a) => a.filter(Boolean);
|
|
||||||
|
|
||||||
const plugins = clean([
|
|
||||||
...cssPlugins,
|
|
||||||
extractMain,
|
|
||||||
!isDev && new OptimizeCssAssetsPlugin(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const rules = [{
|
|
||||||
test: /\.css$/,
|
|
||||||
exclude: /css\/(nojs|view|config|terminal|user-menu|columns.*)\.css/,
|
|
||||||
use: extractMain.extract([
|
|
||||||
'css-loader',
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
...cssPlugins.map(extract), {
|
|
||||||
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
|
||||||
use: {
|
|
||||||
loader: 'url-loader',
|
|
||||||
options: {
|
|
||||||
limit: 100000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
plugins,
|
|
||||||
module: {
|
|
||||||
rules,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function getCSSList(dir) {
|
|
||||||
const base = (a) => basename(a, extname(a));
|
|
||||||
const addDir = (name) => `${dir}/${name}`;
|
|
||||||
const rootDir = join(__dirname, '..');
|
|
||||||
|
|
||||||
return fs.readdirSync(`${rootDir}/css/${dir}`)
|
|
||||||
.map(base)
|
|
||||||
.map(addDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
function extract(extractPlugin) {
|
|
||||||
const {filename} = extractPlugin;
|
|
||||||
|
|
||||||
return {
|
|
||||||
test: RegExp(`css/${filename}`),
|
|
||||||
use: extractPlugin.extract([
|
|
||||||
'css-loader',
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const {env} = process;
|
|
||||||
const isDev = env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
inject: false,
|
|
||||||
template: 'html/index.html',
|
|
||||||
minify: !isDev && getMinifyHtmlOptions(),
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
plugins,
|
|
||||||
};
|
|
||||||
|
|
||||||
function getMinifyHtmlOptions() {
|
|
||||||
return {
|
|
||||||
removeComments: true,
|
|
||||||
removeCommentsFromCDATA: true,
|
|
||||||
removeCDATASectionsFromCDATA: true,
|
|
||||||
collapseWhitespace: true,
|
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
removeAttributeQuotes: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
useShortDoctype: true,
|
|
||||||
removeEmptyAttributes: true,
|
|
||||||
/* оставляем, поскольку у нас
|
|
||||||
* в элемент fm генерируеться
|
|
||||||
* таблица файлов
|
|
||||||
*/
|
|
||||||
removeEmptyElements: false,
|
|
||||||
removeOptionalTags: true,
|
|
||||||
removeScriptTypeAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
|
|
||||||
minifyJS: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
134
.webpack/js.js
134
.webpack/js.js
|
|
@ -1,134 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const {
|
|
||||||
resolve,
|
|
||||||
sep,
|
|
||||||
join,
|
|
||||||
} = require('path');
|
|
||||||
|
|
||||||
const {EnvironmentPlugin} = require('webpack');
|
|
||||||
|
|
||||||
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
|
||||||
|
|
||||||
const dir = './client';
|
|
||||||
const dirModules = './client/modules';
|
|
||||||
const modules = './modules';
|
|
||||||
|
|
||||||
const {env} = process;
|
|
||||||
const {NODE_ENV} = env;
|
|
||||||
const isDev = NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const rootDir = join(__dirname, '..');
|
|
||||||
const dist = resolve(rootDir, 'dist');
|
|
||||||
const distDev = resolve(rootDir, 'dist-dev');
|
|
||||||
const devtool = isDev ? 'eval' : 'source-map';
|
|
||||||
|
|
||||||
const notEmpty = (a) => a;
|
|
||||||
const clean = (array) => array.filter(notEmpty);
|
|
||||||
|
|
||||||
const noParse = (a) => /\.spec\.js$/.test(a);
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
babelrc: false,
|
|
||||||
plugins: [
|
|
||||||
'module:babel-plugin-macros',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const rules = clean([
|
|
||||||
!isDev && {
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
},
|
|
||||||
isDev && {
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
new EnvironmentPlugin({
|
|
||||||
NODE_ENV,
|
|
||||||
}),
|
|
||||||
|
|
||||||
new ServiceWorkerWebpackPlugin({
|
|
||||||
entry: join(__dirname, '..', 'client', 'sw', 'sw.js'),
|
|
||||||
excludes: ['*'],
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const splitChunks = {
|
|
||||||
name: 'cloudcmd.common',
|
|
||||||
chunks: 'all',
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
resolve: {
|
|
||||||
symlinks: false,
|
|
||||||
},
|
|
||||||
devtool,
|
|
||||||
optimization: {
|
|
||||||
splitChunks,
|
|
||||||
},
|
|
||||||
entry: {
|
|
||||||
cloudcmd: `${dir}/cloudcmd.js`,
|
|
||||||
[modules + '/edit']: `${dirModules}/edit.js`,
|
|
||||||
[modules + '/edit-file']: `${dirModules}/edit-file.js`,
|
|
||||||
[modules + '/edit-file-vim']: `${dirModules}/edit-file-vim.js`,
|
|
||||||
[modules + '/edit-names']: `${dirModules}/edit-names.js`,
|
|
||||||
[modules + '/edit-names-vim']: `${dirModules}/edit-names-vim.js`,
|
|
||||||
[modules + '/menu']: `${dirModules}/menu.js`,
|
|
||||||
[modules + '/view']: `${dirModules}/view.js`,
|
|
||||||
[modules + '/help']: `${dirModules}/help.js`,
|
|
||||||
[modules + '/markdown']: `${dirModules}/markdown.js`,
|
|
||||||
[modules + '/config']: `${dirModules}/config/index.js`,
|
|
||||||
[modules + '/contact']: `${dirModules}/contact.js`,
|
|
||||||
[modules + '/upload']: `${dirModules}/upload.js`,
|
|
||||||
[modules + '/operation']: `${dirModules}/operation/index.js`,
|
|
||||||
[modules + '/konsole']: `${dirModules}/konsole.js`,
|
|
||||||
[modules + '/terminal']: `${dirModules}/terminal.js`,
|
|
||||||
[modules + '/terminal-run']: `${dirModules}/terminal-run.js`,
|
|
||||||
[modules + '/cloud']: `${dirModules}/cloud.js`,
|
|
||||||
[modules + '/user-menu']: `${dirModules}/user-menu/index.js`,
|
|
||||||
[modules + '/polyfill']: `${dirModules}/polyfill.js`,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: '[name].js',
|
|
||||||
path: isDev ? distDev : dist,
|
|
||||||
pathinfo: isDev,
|
|
||||||
devtoolModuleFilenameTemplate,
|
|
||||||
publicPath: '/dist/',
|
|
||||||
},
|
|
||||||
externals: [
|
|
||||||
externals,
|
|
||||||
],
|
|
||||||
module: {
|
|
||||||
rules,
|
|
||||||
noParse,
|
|
||||||
},
|
|
||||||
plugins,
|
|
||||||
performance: {
|
|
||||||
maxEntrypointSize: 600000,
|
|
||||||
maxAssetSize: 600000,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function externals(context, request, fn) {
|
|
||||||
if (!isDev)
|
|
||||||
return fn();
|
|
||||||
|
|
||||||
const list = [];
|
|
||||||
|
|
||||||
if (list.includes(request))
|
|
||||||
return fn(null, request);
|
|
||||||
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
|
|
||||||
function devtoolModuleFilenameTemplate(info) {
|
|
||||||
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
|
|
||||||
return `file://cloudcmd/${resource}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -22,12 +22,14 @@
|
||||||
"Iptables",
|
"Iptables",
|
||||||
"JitSu",
|
"JitSu",
|
||||||
"Node",
|
"Node",
|
||||||
|
"IO",
|
||||||
"Olena",
|
"Olena",
|
||||||
"TarZak",
|
"TarZak",
|
||||||
"Termux",
|
"Termux",
|
||||||
"Zalitok",
|
"Zalitok",
|
||||||
"WebSocket",
|
"WebSocket",
|
||||||
"auth",
|
"auth",
|
||||||
|
"autostart",
|
||||||
"binded",
|
"binded",
|
||||||
"cd",
|
"cd",
|
||||||
"cloudcmd",
|
"cloudcmd",
|
||||||
|
|
@ -44,6 +46,7 @@
|
||||||
"gz",
|
"gz",
|
||||||
"io",
|
"io",
|
||||||
"js",
|
"js",
|
||||||
|
"linux",
|
||||||
"maintainers",
|
"maintainers",
|
||||||
"markdown",
|
"markdown",
|
||||||
"microservice",
|
"microservice",
|
||||||
|
|
@ -53,6 +56,8 @@
|
||||||
"nginx",
|
"nginx",
|
||||||
"npm",
|
"npm",
|
||||||
"or io",
|
"or io",
|
||||||
|
"patreon",
|
||||||
|
"rc",
|
||||||
"refactor",
|
"refactor",
|
||||||
"sexualized",
|
"sexualized",
|
||||||
"sslPort",
|
"sslPort",
|
||||||
|
|
@ -61,6 +66,7 @@
|
||||||
"v0",
|
"v0",
|
||||||
"v1",
|
"v1",
|
||||||
"v2",
|
"v2",
|
||||||
"yml"
|
"yml",
|
||||||
|
"systemd"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
- Using welcoming and inclusive language
|
||||||
* Being respectful of differing viewpoints and experiences
|
- Being respectful of differing viewpoints and experiences
|
||||||
* Gracefully accepting constructive criticism
|
- Gracefully accepting constructive criticism
|
||||||
* Focusing on what is best for the community
|
- Focusing on what is best for the community
|
||||||
* Showing empathy towards other community members
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
- 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
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
Commit
|
## Commit
|
||||||
---------------
|
|
||||||
Format of the commit message: **type(scope) subject**
|
Format of the commit message: **type(scope) subject**
|
||||||
|
|
||||||
**Type**:
|
**Type**:
|
||||||
- feature(scope) subject
|
|
||||||
- fix(scope) subject
|
- feature: scope: subject
|
||||||
- docs(scope) subject
|
- fix: scope: subject
|
||||||
- refactor(scope) subject
|
- docs: scope: subject
|
||||||
- test(scope) subject
|
- refactor: scope: subject
|
||||||
- chore(scope) subject
|
- test: scope: subject
|
||||||
|
- chore: scope: subject
|
||||||
|
|
||||||
**Scope**:
|
**Scope**:
|
||||||
Scope could be anything specifying place of the commit change.
|
Scope could be anything specifying place of the commit change.
|
||||||
For example util, console, view, edit, style etc...
|
For example util, console, view, edit, style etc...
|
||||||
|
|
||||||
**Subject text**:
|
**Subject text**:
|
||||||
|
|
||||||
- use imperative, present tense: “change” not “changed” nor “changes”
|
- use imperative, present tense: “change” not “changed” nor “changes”
|
||||||
- don't capitalize first letter
|
- don't capitalize first letter
|
||||||
- no dot (.) at the end
|
- no dot (.) at the end
|
||||||
|
|
@ -23,5 +25,6 @@ For example util, console, view, edit, style etc...
|
||||||
- includes motivation for the change and contrasts with previous behavior
|
- includes motivation for the change and contrasts with previous behavior
|
||||||
|
|
||||||
**Examples**:
|
**Examples**:
|
||||||
- [fix(style) .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
|
|
||||||
- [fix(console) dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec)
|
- [fix: style: .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
|
||||||
|
- [fix: console: dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec)
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
(The MIT License)
|
(The MIT License)
|
||||||
|
|
||||||
Copyright (c) 2012-2019 Coderaiser <mnemonic.enemy@gmail.com>
|
Copyright (c) 2012-2025 Coderaiser <mnemonic.enemy@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|
|
||||||
69
README.md
69
README.md
|
|
@ -1,25 +1,22 @@
|
||||||
# Cloud Commander v14.6.0 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [](https://app.fossa.io/projects/git%2Bgithub.com%2Fcoderaiser%2Fcloudcmd?ref=badge_shield)
|
# Cloud Commander v19.19.1 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![Deploy on InstaPods][DeployInstaPodsIMG]][DeployInstaPodsURL]
|
||||||
|
|
||||||
### [Main][MainURL] [Blog][BlogURL] Live([Heroku][HerokuURL])
|
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
|
||||||
|
|
||||||
|
[MainURL]: https://cloudcmd.io "Main"
|
||||||
|
[BlogURL]: https://blog.cloudcmd.io "Blog"
|
||||||
|
[SupportURL]: https://patreon.com/coderaiser "Patreon"
|
||||||
|
[DemoURL]: https://cloudcmd-zdp6.onrender.com
|
||||||
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
||||||
[MainURL]: http://cloudcmd.io "Main"
|
[BuildStatusURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml "Build Status"
|
||||||
[BlogURL]: http://blog.cloudcmd.io "Blog"
|
[BuildStatusIMGURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml/badge.svg
|
||||||
[HerokuURL]: https://cloudcmd.herokuapp.com/ "Heroku"
|
|
||||||
[BuildStatusURL]: https://travis-ci.org/coderaiser/cloudcmd "Build Status"
|
|
||||||
[BuildStatusIMGURL]: https://img.shields.io/travis/coderaiser/cloudcmd.svg?style=flat-squere&longCache=true
|
|
||||||
|
|
||||||
[BuildAppveyorURL]: https://ci.appveyor.com/project/coderaiser/cloudcmd
|
|
||||||
[BuildAppveyorIMGURL]: https://ci.appveyor.com/api/projects/status/tse6sc8dxrqxqehi?svg=true
|
|
||||||
|
|
||||||
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
||||||
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
||||||
|
|
||||||
[GitterURL]: https://gitter.im/cloudcmd/hello
|
[GitterURL]: https://gitter.im/cloudcmd/hello
|
||||||
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
||||||
|
|
||||||
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
||||||
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
|
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
|
||||||
|
[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue
|
||||||
|
[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd
|
||||||
|
|
||||||
**Cloud Commander** a file manager for the web with console and editor.
|
**Cloud Commander** a file manager for the web with console and editor.
|
||||||
|
|
||||||
|
|
@ -30,6 +27,7 @@
|
||||||
```
|
```
|
||||||
npm i cloudcmd -g
|
npm i cloudcmd -g
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
|
|
||||||
For starting just type in console:
|
For starting just type in console:
|
||||||
|
|
@ -47,8 +45,8 @@ Open url `http://localhost:8000` in browser.
|
||||||
You will see something similar to this.
|
You will see something similar to this.
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
`Cloud Commander` could be easily deployed to [Heroku][DeployURL].
|
`Cloud Commander` could be easily deployed to [Heroku][DeployURL].
|
||||||
|
|
||||||
[![Deploy][DeployIMG]][DeployURL]
|
[![Deploy][DeployIMG]][DeployURL]
|
||||||
|
|
@ -72,17 +70,19 @@ npm i cloudcmd express socket.io -S
|
||||||
And create `index.js`:
|
And create `index.js`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const http = require('http');
|
import http from 'node:http';
|
||||||
const cloudcmd = require('cloudcmd');
|
import {cloudcmd} from 'cloudcmd';
|
||||||
const io = require('socket.io');
|
import {Server} from 'socket.io';
|
||||||
const app = require('express')();
|
import express from 'express';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
const port = 1337;
|
const port = 1337;
|
||||||
const prefix = '/';
|
const prefix = '/';
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const socket = io.listen(server, {
|
const socket = new Server(server, {
|
||||||
path: `${prefix}/socket.io`
|
path: `${prefix}socket.io`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
|
@ -93,8 +93,8 @@ const filePicker = {
|
||||||
data: {
|
data: {
|
||||||
FilePicker: {
|
FilePicker: {
|
||||||
key: 'key',
|
key: 'key',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// override option from json/modules.json
|
// override option from json/modules.json
|
||||||
|
|
@ -109,7 +109,7 @@ const {
|
||||||
|
|
||||||
const configManager = createConfigManager({
|
const configManager = createConfigManager({
|
||||||
configPath,
|
configPath,
|
||||||
}),
|
});
|
||||||
|
|
||||||
app.use(prefix, cloudcmd({
|
app.use(prefix, cloudcmd({
|
||||||
socket, // used by Config, Edit (optional) and Console (required)
|
socket, // used by Config, Edit (optional) and Console (required)
|
||||||
|
|
@ -121,12 +121,21 @@ app.use(prefix, cloudcmd({
|
||||||
server.listen(port);
|
server.listen(port);
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker
|
## Docker
|
||||||
---------------
|
|
||||||
|
The docker images are provided for multiple architectures and types. The following list shows all existing images:
|
||||||
|
|
||||||
|
| Architecture | Type |
|
||||||
|
|----------------|--------------|
|
||||||
|
| amd64 | linux |
|
||||||
|
| arm64 (arm/v8) | linux |
|
||||||
|
| amd64 | linux-alpine |
|
||||||
|
| arm64 (arm/v8) | linux-alpine |
|
||||||
|
|
||||||
`Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way:
|
`Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run -t --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd
|
docker run -it --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd
|
||||||
```
|
```
|
||||||
|
|
||||||
Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`,
|
Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`,
|
||||||
|
|
@ -152,11 +161,15 @@ When you create this file run:
|
||||||
docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
Get involved
|
## Documentation
|
||||||
---------------
|
|
||||||
|
More documentation you can find on https://cloudcmd.io/.
|
||||||
|
|
||||||
|
## Get involved
|
||||||
|
|
||||||
There is a lot ways to be involved in `Cloud Commander` development:
|
There is a lot ways to be involved in `Cloud Commander` development:
|
||||||
|
|
||||||
|
- support project on patreon: https://patreon.com/coderaiser;
|
||||||
- if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue");
|
- if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue");
|
||||||
- if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request");
|
- if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request");
|
||||||
- if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki");
|
- if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki");
|
||||||
|
|
|
||||||
6
app.json
6
app.json
|
|
@ -11,6 +11,7 @@
|
||||||
"folder",
|
"folder",
|
||||||
"orthodox"
|
"orthodox"
|
||||||
],
|
],
|
||||||
|
"stack": "heroku-18",
|
||||||
"env": {
|
"env": {
|
||||||
"NPM_CONFIG_PRODUCTION": {
|
"NPM_CONFIG_PRODUCTION": {
|
||||||
"description": "Keep false to install devDependencies and build frontend",
|
"description": "Keep false to install devDependencies and build frontend",
|
||||||
|
|
@ -62,11 +63,6 @@
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"CLOUDCMD_CONFIG_DIALOG": {
|
|
||||||
"description": "show config dialog",
|
|
||||||
"value": "false",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"CLOUDCMD_ONE_FILE_PANEL": {
|
"CLOUDCMD_ONE_FILE_PANEL": {
|
||||||
"description": "show one file panel",
|
"description": "show one file panel",
|
||||||
"value": "false",
|
"value": "false",
|
||||||
|
|
|
||||||
256
bin/cloudcmd.js
256
bin/cloudcmd.js
|
|
@ -1,37 +1,48 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
'use strict';
|
import process from 'node:process';
|
||||||
|
import {promisify} from 'node:util';
|
||||||
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import parse from 'yargs-parser';
|
||||||
|
import exit from '../server/exit.js';
|
||||||
|
import {createConfig, configPath} from '../server/config.js';
|
||||||
|
import * as env from '../server/env.js';
|
||||||
|
import prefixer from '../server/prefixer.js';
|
||||||
|
import * as validate from '../server/validate.js';
|
||||||
|
import Info from '../package.json' with {
|
||||||
|
type: 'json',
|
||||||
|
};
|
||||||
|
|
||||||
const Info = require('../package');
|
process.on('unhandledRejection', exit);
|
||||||
const DIR_SERVER = '../server/';
|
|
||||||
|
|
||||||
const {promisify} = require('util');
|
const isUndefined = (a) => typeof a === 'undefined';
|
||||||
const tryToCatch = require('try-to-catch');
|
|
||||||
|
|
||||||
const exit = require(DIR_SERVER + 'exit');
|
|
||||||
const {
|
|
||||||
createConfig,
|
|
||||||
configPath,
|
|
||||||
} = require(DIR_SERVER + 'config');
|
|
||||||
|
|
||||||
const config = createConfig({
|
|
||||||
configPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
const env = require(DIR_SERVER + 'env');
|
|
||||||
const prefixer = require(DIR_SERVER + '/prefixer');
|
|
||||||
|
|
||||||
const choose = (a, b) => {
|
const choose = (a, b) => {
|
||||||
if (a === undefined)
|
if (isUndefined(a))
|
||||||
return b;
|
return b;
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on('unhandledRejection', exit);
|
const config = createConfig({
|
||||||
|
configPath,
|
||||||
|
});
|
||||||
|
|
||||||
const {argv} = process;
|
const maybeRoot = (a) => {
|
||||||
const args = require('minimist')(argv.slice(2), {
|
if (a === '.')
|
||||||
|
return process.cwd();
|
||||||
|
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
|
||||||
|
const yargsOptions = {
|
||||||
|
configuration: {
|
||||||
|
'strip-aliased': true,
|
||||||
|
'strip-dashed': true,
|
||||||
|
},
|
||||||
|
coerce: {
|
||||||
|
root: maybeRoot,
|
||||||
|
},
|
||||||
string: [
|
string: [
|
||||||
'name',
|
'name',
|
||||||
'port',
|
'port',
|
||||||
|
|
@ -46,6 +57,8 @@ const args = require('minimist')(argv.slice(2), {
|
||||||
'terminal-path',
|
'terminal-path',
|
||||||
'terminal-command',
|
'terminal-command',
|
||||||
'columns',
|
'columns',
|
||||||
|
'menu',
|
||||||
|
'theme',
|
||||||
'import-url',
|
'import-url',
|
||||||
'import-token',
|
'import-token',
|
||||||
'export-token',
|
'export-token',
|
||||||
|
|
@ -60,6 +73,7 @@ const args = require('minimist')(argv.slice(2), {
|
||||||
'open',
|
'open',
|
||||||
'config-dialog',
|
'config-dialog',
|
||||||
'config-auth',
|
'config-auth',
|
||||||
|
'config-port',
|
||||||
'console',
|
'console',
|
||||||
'sync-console-path',
|
'sync-console-path',
|
||||||
'contact',
|
'contact',
|
||||||
|
|
@ -69,6 +83,7 @@ const args = require('minimist')(argv.slice(2), {
|
||||||
'confirm-copy',
|
'confirm-copy',
|
||||||
'confirm-move',
|
'confirm-move',
|
||||||
'show-config',
|
'show-config',
|
||||||
|
'show-dot-files',
|
||||||
'show-file-name',
|
'show-file-name',
|
||||||
'vim',
|
'vim',
|
||||||
'keys-panel',
|
'keys-panel',
|
||||||
|
|
@ -77,65 +92,70 @@ const args = require('minimist')(argv.slice(2), {
|
||||||
'import',
|
'import',
|
||||||
'import-listen',
|
'import-listen',
|
||||||
'log',
|
'log',
|
||||||
|
'zip',
|
||||||
'dropbox',
|
'dropbox',
|
||||||
],
|
],
|
||||||
default: {
|
default: {
|
||||||
'server': true,
|
'server': true,
|
||||||
'name' : choose(env('name'), config('name')),
|
'name': choose(env.parse('name'), config('name')),
|
||||||
'auth': choose(env.bool('auth'), config('auth')),
|
'auth': choose(env.bool('auth'), config('auth')),
|
||||||
'port': config('port'),
|
'port': config('port'),
|
||||||
'online': config('online'),
|
'online': config('online'),
|
||||||
'open': choose(env.bool('open'), config('open')),
|
'open': choose(env.bool('open'), config('open')),
|
||||||
'editor' : env('editor') || config('editor'),
|
'editor': env.parse('editor') || config('editor'),
|
||||||
|
'menu': env.parse('menu') || config('menu'),
|
||||||
'packer': config('packer') || 'tar',
|
'packer': config('packer') || 'tar',
|
||||||
'zip': config('zip'),
|
'zip': config('zip'),
|
||||||
'username' : env('username') || config('username'),
|
'username': env.parse('username') || config('username'),
|
||||||
'root' : choose(env('root'), config('root')),
|
'root': choose(env.parse('root'), config('root')),
|
||||||
'prefix' : choose(env('cloudcmd_prefix'), config('prefix')),
|
'prefix': choose(env.parse('prefix'), config('prefix')),
|
||||||
'console': choose(env.bool('console'), config('console')),
|
'console': choose(env.bool('console'), config('console')),
|
||||||
'contact': choose(env.bool('contact'), config('contact')),
|
'contact': choose(env.bool('contact'), config('contact')),
|
||||||
'terminal': choose(env.bool('terminal'), config('terminal')),
|
'terminal': choose(env.bool('terminal'), config('terminal')),
|
||||||
'columns' : env('columns') || config('columns') || '',
|
'columns': env.parse('columns') || config('columns') || '',
|
||||||
|
'theme': env.parse('theme') || config('theme') || '',
|
||||||
'vim': choose(env.bool('vim'), config('vim')),
|
'vim': choose(env.bool('vim'), config('vim')),
|
||||||
'log' : config('log'),
|
'log': choose(env.bool('log'), config('log')),
|
||||||
|
|
||||||
'import-url': env('import_url') || config('importUrl'),
|
'import-url': env.parse('import_url') || config('importUrl'),
|
||||||
'import-listen': choose(env.bool('import_listen'), config('importListen')),
|
'import-listen': choose(env.bool('import_listen'), config('importListen')),
|
||||||
'import': choose(env.bool('import'), config('import')),
|
'import': choose(env.bool('import'), config('import')),
|
||||||
'export': choose(env.bool('export'), config('export')),
|
'export': choose(env.bool('export'), config('export')),
|
||||||
|
|
||||||
'prefix-socket': config('prefixSocket'),
|
'prefix-socket': config('prefixSocket'),
|
||||||
|
'show-dot-files': choose(env.bool('show_dot_files'), config('showDotFiles')),
|
||||||
'show-file-name': choose(env.bool('show_file_name'), config('showFileName')),
|
'show-file-name': choose(env.bool('show_file_name'), config('showFileName')),
|
||||||
'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')),
|
'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')),
|
||||||
'config-dialog': choose(env.bool('config_dialog'), config('configDialog')),
|
'config-dialog': choose(env.bool('config_dialog'), config('configDialog')),
|
||||||
'config-auth': choose(env.bool('config_auth'), config('configAuth')),
|
'config-auth': choose(env.bool('config_auth'), config('configAuth')),
|
||||||
'terminal-path': env('terminal_path') || config('terminalPath'),
|
'config-port': choose(env.bool('config_port'), config('configPort')),
|
||||||
'terminal-command': env('terminal_command') || config('terminalCommand'),
|
'terminal-path': env.parse('terminal_path') || config('terminalPath'),
|
||||||
|
'terminal-command': env.parse('terminal_command') || config('terminalCommand'),
|
||||||
'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')),
|
'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')),
|
||||||
'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')),
|
'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')),
|
||||||
'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')),
|
'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')),
|
||||||
'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')),
|
'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')),
|
||||||
'keys-panel': env.bool('keys_panel') || config('keysPanel'),
|
'keys-panel': env.bool('keys_panel') || config('keysPanel'),
|
||||||
'import-token': env('import_token') || config('importToken'),
|
'import-token': env.parse('import_token') || config('importToken'),
|
||||||
'export-token': env('export_token') || config('exportToken'),
|
'export-token': env.parse('export_token') || config('exportToken'),
|
||||||
|
|
||||||
'dropbox': config('dropbox'),
|
'dropbox': config('dropbox'),
|
||||||
'dropbox-token': config('dropboxToken'),
|
'dropbox-token': config('dropboxToken') || '',
|
||||||
},
|
},
|
||||||
alias: {
|
alias: {
|
||||||
v: 'version',
|
version: 'v',
|
||||||
h: 'help',
|
help: 'h',
|
||||||
p: 'password',
|
password: 'p',
|
||||||
o: 'online',
|
online: 'o',
|
||||||
u: 'username',
|
username: 'u',
|
||||||
s: 'save',
|
save: 's',
|
||||||
a: 'auth',
|
auth: 'a',
|
||||||
c: 'config',
|
config: 'c',
|
||||||
},
|
},
|
||||||
unknown: (cmd) => {
|
};
|
||||||
exit('\'%s\' is not a cloudcmd option. See \'cloudcmd --help\'.', cmd);
|
|
||||||
},
|
const {argv} = process;
|
||||||
});
|
const args = parse(argv.slice(2), yargsOptions);
|
||||||
|
|
||||||
if (args.version)
|
if (args.version)
|
||||||
version();
|
version();
|
||||||
|
|
@ -145,8 +165,21 @@ else
|
||||||
main();
|
main();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
const {validateArgs} = await import('@putout/cli-validate-args');
|
||||||
|
|
||||||
|
const error = await validateArgs(args, [
|
||||||
|
...yargsOptions.boolean,
|
||||||
|
...yargsOptions.string,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return exit(error);
|
||||||
|
|
||||||
if (args.repl)
|
if (args.repl)
|
||||||
repl();
|
await repl();
|
||||||
|
|
||||||
|
validate.columns(args.columns);
|
||||||
|
validate.theme(args.theme);
|
||||||
|
|
||||||
port(args.port);
|
port(args.port);
|
||||||
|
|
||||||
|
|
@ -156,37 +189,41 @@ async function main() {
|
||||||
config('open', args.open);
|
config('open', args.open);
|
||||||
config('username', args.username);
|
config('username', args.username);
|
||||||
config('console', args.console);
|
config('console', args.console);
|
||||||
config('syncConsolePath', args['sync-console-path']);
|
config('syncConsolePath', args.syncConsolePath);
|
||||||
config('showFileName', args['show-file-name']);
|
config('showDotFiles', args.showDotFiles);
|
||||||
|
config('showFileName', args.showFileName);
|
||||||
config('contact', args.contact);
|
config('contact', args.contact);
|
||||||
config('terminal', args.terminal);
|
config('terminal', args.terminal);
|
||||||
config('terminalPath', args['terminal-path']);
|
config('terminalPath', args.terminalPath);
|
||||||
config('terminalCommand', args['terminal-command']);
|
config('terminalCommand', args.terminalCommand);
|
||||||
config('terminalAutoRestart', args['terminal-auto-restart']);
|
config('terminalAutoRestart', args.terminalAutoRestart);
|
||||||
config('editor', args.editor);
|
config('editor', args.editor);
|
||||||
|
config('menu', args.menu);
|
||||||
config('prefix', prefixer(args.prefix));
|
config('prefix', prefixer(args.prefix));
|
||||||
config('prefixSocket', prefixer(args['prefix-socket']));
|
config('prefixSocket', prefixer(args.prefixSocket));
|
||||||
config('root', args.root || '/');
|
config('root', args.root || '/');
|
||||||
config('vim', args.vim);
|
config('vim', args.vim);
|
||||||
|
config('theme', args.theme);
|
||||||
config('columns', args.columns);
|
config('columns', args.columns);
|
||||||
config('log', args.log);
|
config('log', args.log);
|
||||||
config('confirmCopy', args['confirm-copy']);
|
config('confirmCopy', args.confirmCopy);
|
||||||
config('confirmMove', args['confirm-move']);
|
config('confirmMove', args.confirmMove);
|
||||||
config('oneFilePanel', args['one-file-panel']);
|
config('oneFilePanel', args.oneFilePanel);
|
||||||
config('configDialog', args['config-dialog']);
|
config('configDialog', args.configDialog);
|
||||||
config('configAuth', args['config-auth']);
|
config('configAuth', args.configAuth);
|
||||||
config('keysPanel', args['keys-panel']);
|
config('configPort', args.configPort);
|
||||||
|
config('keysPanel', args.keysPanel);
|
||||||
config('export', args.export);
|
config('export', args.export);
|
||||||
config('exportToken', args['export-token']);
|
config('exportToken', args.exportToken);
|
||||||
config('import', args.import);
|
config('import', args.import);
|
||||||
config('importToken', args['import-token']);
|
config('importToken', args.importToken);
|
||||||
config('importListen', args['import-listen']);
|
config('importListen', args.importListen);
|
||||||
config('importUrl', args['import-url']);
|
config('importUrl', args.importUrl);
|
||||||
|
|
||||||
config('dropbox', args['dropbox']);
|
config('dropbox', args.dropbox);
|
||||||
config('dropboxToken', args['dropbox-token'] || '');
|
config('dropboxToken', args.dropboxToken);
|
||||||
|
|
||||||
readConfig(args.config);
|
await readConfig(args.config);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
root: config('root'),
|
root: config('root'),
|
||||||
|
|
@ -195,42 +232,44 @@ async function main() {
|
||||||
prefix: config('prefix'),
|
prefix: config('prefix'),
|
||||||
prefixSocket: config('prefixSocket'),
|
prefixSocket: config('prefixSocket'),
|
||||||
columns: config('columns'),
|
columns: config('columns'),
|
||||||
|
theme: config('theme'),
|
||||||
|
menu: config('menu'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const password = env('password') || args.password;
|
const password = env.parse('password') || args.password;
|
||||||
|
|
||||||
if (password)
|
if (password)
|
||||||
config('password', getPassword(password));
|
config('password', await getPassword(password));
|
||||||
|
|
||||||
validateRoot(options.root, config);
|
validateRoot(options.root, config);
|
||||||
|
|
||||||
if (args['show-config'])
|
if (args.showConfig)
|
||||||
showConfig();
|
await showConfig();
|
||||||
|
|
||||||
const distribute = require('../server/distribute');
|
const {distributeImport} = await import('../server/distribute/import.js');
|
||||||
const importConfig = promisify(distribute.import);
|
const importConfig = promisify(distributeImport);
|
||||||
|
|
||||||
await importConfig(config);
|
await start(options, config);
|
||||||
|
|
||||||
if (args.save)
|
if (args.save)
|
||||||
config.write();
|
config.write();
|
||||||
|
|
||||||
start(options, config);
|
|
||||||
await tryToCatch(checkUpdate);
|
await tryToCatch(checkUpdate);
|
||||||
|
await importConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRoot(root, config) {
|
function validateRoot(root, config) {
|
||||||
const validate = require(DIR_SERVER + 'validate');
|
|
||||||
validate.root(root, config);
|
validate.root(root, config);
|
||||||
|
|
||||||
if (root === '/')
|
if (root === '/')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (config('log'))
|
||||||
console.log(`root: ${root}`);
|
console.log(`root: ${root}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPassword(password) {
|
async function getPassword(password) {
|
||||||
const criton = require('criton');
|
const {default: criton} = await import('criton');
|
||||||
return criton(password, config('algo'));
|
return criton(password, config('algo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,13 +277,11 @@ function version() {
|
||||||
console.log('v' + Info.version);
|
console.log('v' + Info.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(options, config) {
|
async function start(options, config) {
|
||||||
const SERVER = DIR_SERVER + 'server';
|
|
||||||
|
|
||||||
if (!args.server)
|
if (!args.server)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const server = require(SERVER);
|
const {default: server} = await import('../server/server.js');
|
||||||
server(options, config);
|
server(options, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,38 +294,38 @@ function port(arg) {
|
||||||
exit('cloudcmd --port: should be a number');
|
exit('cloudcmd --port: should be a number');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showConfig() {
|
async function showConfig() {
|
||||||
const show = require('../server/show-config');
|
const {showConfig} = await import('../server/show-config.js');
|
||||||
const data = show(config('*'));
|
const data = showConfig(config('*'));
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readConfig(name) {
|
async function readConfig(name) {
|
||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const fs = require('fs');
|
const {default: forEachKey} = await import('for-each-key');
|
||||||
const tryCatch = require('try-catch');
|
|
||||||
const jju = require('jju');
|
|
||||||
const forEachKey = require('for-each-key');
|
|
||||||
|
|
||||||
const readjsonSync = (name) => jju.parse(fs.readFileSync(name, 'utf8'), {
|
const data = await import(name, {
|
||||||
mode: 'json',
|
with: {
|
||||||
|
type: 'json',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [error, data] = tryCatch(readjsonSync, name);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return exit(error.message);
|
|
||||||
|
|
||||||
forEachKey(config, data);
|
forEachKey(config, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function help() {
|
async function help() {
|
||||||
const bin = require('../json/help');
|
const {default: bin} = await import('../json/help.json', {
|
||||||
const forEachKey = require('for-each-key');
|
with: {
|
||||||
const currify = require('currify');
|
type: 'json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {default: forEachKey} = await import('for-each-key');
|
||||||
|
const {default: currify} = await import('currify');
|
||||||
|
|
||||||
const usage = 'Usage: cloudcmd [options]';
|
const usage = 'Usage: cloudcmd [options]';
|
||||||
const url = Info.homepage;
|
const url = Info.homepage;
|
||||||
const log = currify((a, b, c) => console.log(a, b, c));
|
const log = currify((a, b, c) => console.log(a, b, c));
|
||||||
|
|
@ -299,28 +336,27 @@ function help() {
|
||||||
console.log('\nGeneral help using Cloud Commander: <%s>', url);
|
console.log('\nGeneral help using Cloud Commander: <%s>', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function repl() {
|
async function repl() {
|
||||||
console.log('REPL mode enabled (telnet localhost 1337)');
|
console.log('REPL mode enabled (telnet localhost 1337)');
|
||||||
require(DIR_SERVER + 'repl');
|
await import('../server/repl.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkUpdate() {
|
async function checkUpdate() {
|
||||||
const load = require('package-json');
|
const {default: load} = await import('package-json');
|
||||||
|
|
||||||
const {version} = await load(Info.name, 'latest');
|
const {version} = await load(Info.name, 'latest');
|
||||||
showUpdateInfo(version);
|
|
||||||
|
await showUpdateInfo(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUpdateInfo(version) {
|
async function showUpdateInfo(version) {
|
||||||
if (version === Info.version)
|
if (version === Info.version)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const chalk = require('chalk');
|
const {default: chalk} = await import('chalk');
|
||||||
|
|
||||||
const latestVersion = chalk.green.bold('v' + version);
|
const latestVersion = chalk.green.bold(`v${version}`);
|
||||||
const latest = `update available: ${latestVersion}`;
|
const latest = `update available: ${latestVersion}`;
|
||||||
const current = chalk.dim(`(current: v${Info.version})`);
|
const current = chalk.dim(`(current: v${Info.version})`);
|
||||||
|
|
||||||
console.log('%s %s', latest, current);
|
console.log('%s %s', latest, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
26
bin/cloudcmd.spec.js
Normal file
26
bin/cloudcmd.spec.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {spawnSync} from 'node:child_process';
|
||||||
|
import {test} from 'supertape';
|
||||||
|
import info from '../package.json' with {
|
||||||
|
type: 'json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const cliPath = new URL('cloudcmd.js', import.meta.url).pathname;
|
||||||
|
|
||||||
|
test('cloudcmd: bin: cli: -h', (t) => {
|
||||||
|
const {stdout} = spawnSync(cliPath, ['-h'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.match(stdout, `Options`);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: bin: cli: -v', (t) => {
|
||||||
|
const {version} = info;
|
||||||
|
const {stdout} = spawnSync(cliPath, ['-v'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.match(stdout, `v${version}`);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,67 +1,72 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
'use strict';
|
import {promisify} from 'node:util';
|
||||||
|
import process from 'node:process';
|
||||||
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import minor from 'minor';
|
||||||
|
import _place from 'place';
|
||||||
|
import {rendy} from 'rendy';
|
||||||
|
import shortdate from 'shortdate';
|
||||||
|
import Info from '../package.json' with {
|
||||||
|
type: 'json',
|
||||||
|
};
|
||||||
|
|
||||||
const DIR = '../';
|
const place = promisify(_place);
|
||||||
const Info = require(DIR + 'package');
|
|
||||||
|
|
||||||
const minor = require('minor');
|
await main();
|
||||||
const place = require('place');
|
|
||||||
const rendy = require('rendy');
|
|
||||||
const shortdate = require('shortdate');
|
|
||||||
|
|
||||||
const ERROR = Error('ERROR: version is missing. release --patch|--minor|--major');
|
async function main() {
|
||||||
|
const history = '## Version history\n\n';
|
||||||
main((error) => {
|
|
||||||
if (error)
|
|
||||||
console.error(error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
function main(callback) {
|
|
||||||
const history = 'Version history\n---------------\n';
|
|
||||||
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
||||||
const template = '- *{{ date }}*, ' +
|
|
||||||
|
const template = '- ' +
|
||||||
|
'*{{ date }}*, ' +
|
||||||
'**[v{{ version }}]' +
|
'**[v{{ version }}]' +
|
||||||
'(' + link + 'v{{ version }})**\n';
|
'(' +
|
||||||
|
link +
|
||||||
|
'v{{ version }})**\n';
|
||||||
|
|
||||||
const {version} = Info;
|
const {version} = Info;
|
||||||
|
|
||||||
cl((error, versionNew) => {
|
const [error, versionNew] = await tryToCatch(cl);
|
||||||
if (error)
|
|
||||||
return callback(error);
|
|
||||||
|
|
||||||
replaceVersion('README.md', version, versionNew, callback);
|
if (error)
|
||||||
replaceVersion('HELP.md', version, versionNew, () => {
|
return console.error(error);
|
||||||
const historyNew = history + rendy(template, {
|
|
||||||
|
await replaceVersion('README.md', version, versionNew);
|
||||||
|
await replaceVersion('HELP.md', version, versionNew);
|
||||||
|
|
||||||
|
const historyNew = history +
|
||||||
|
rendy(template, {
|
||||||
date: shortdate(),
|
date: shortdate(),
|
||||||
version: versionNew,
|
version: versionNew,
|
||||||
});
|
});
|
||||||
|
|
||||||
replaceVersion('HELP.md', history, historyNew, callback);
|
await replaceVersion('HELP.md', history, historyNew);
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceVersion(name, version, versionNew, callback) {
|
async function replaceVersion(name, version, versionNew) {
|
||||||
place(name, version, versionNew, (error) => {
|
const [error] = await tryToCatch(place, name, version, versionNew);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return callback(error);
|
return console.error(error);
|
||||||
|
|
||||||
callback(null, 'done: ' + name);
|
console.log(`done: ${name}`);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cl(callback) {
|
async function cl() {
|
||||||
const {argv} = process;
|
const {argv} = process;
|
||||||
const length = argv.length - 1;
|
const length = argv.length - 1;
|
||||||
const last = process.argv[length];
|
const last = process.argv[length];
|
||||||
const regExp = /^--(major|minor|patch)$/;
|
const regExp = /^--(major|minor|patch)$/;
|
||||||
const [, match] = last.match(regExp) || [];
|
const [, match] = last.match(regExp) || [];
|
||||||
|
|
||||||
if (!regExp.test(last))
|
console.log(last);
|
||||||
return callback(ERROR);
|
|
||||||
|
|
||||||
callback(null, getVersionNew(last, match));
|
if (!regExp.test(last))
|
||||||
|
throw Error('ERROR: version is missing. release --patch|--minor|--major');
|
||||||
|
|
||||||
|
return getVersionNew(last, match);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionNew(last, match) {
|
function getVersionNew(last, match) {
|
||||||
|
|
@ -70,4 +75,3 @@ function getVersionNew(last, match) {
|
||||||
|
|
||||||
return last.substr(3);
|
return last.substr(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
185
client/client.js
185
client/client.js
|
|
@ -1,74 +1,59 @@
|
||||||
'use strict';
|
import process from 'node:process';
|
||||||
|
import Emitify from 'emitify';
|
||||||
/* global DOM */
|
import inherits from 'inherits';
|
||||||
|
import {rendy} from 'rendy';
|
||||||
const Emitify = require('emitify');
|
import load from 'load.js';
|
||||||
const inherits = require('inherits');
|
import {tryToCatch} from 'try-to-catch';
|
||||||
const rendy = require('rendy');
|
import {addSlashToEnd} from 'format-io';
|
||||||
const load = require('load.js');
|
import pascalCase from 'just-pascal-case';
|
||||||
const tryToCatch = require('try-to-catch');
|
import currify from 'currify';
|
||||||
|
import {
|
||||||
const pascalCase = require('just-pascal-case');
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const Images = require('./dom/images');
|
|
||||||
const {unregisterSW} = require('./sw/register');
|
|
||||||
const getJsonFromFileTable = require('./get-json-from-file-table');
|
|
||||||
|
|
||||||
const currify = require('currify');
|
|
||||||
|
|
||||||
const noJS = (a) => a.replace(/.js$/, '');
|
|
||||||
|
|
||||||
const {
|
|
||||||
apiURL,
|
apiURL,
|
||||||
formatMsg,
|
formatMsg,
|
||||||
buildFromJSON,
|
buildFromJSON,
|
||||||
} = require('../common/cloudfunc');
|
} from '#common/cloudfunc';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
import {unregisterSW} from './sw/register.js';
|
||||||
|
import {getJsonFromFileTable} from './get-json-from-file-table.js';
|
||||||
|
import {Key} from './key/index.js';
|
||||||
|
import {loadModule} from './load-module.js';
|
||||||
|
|
||||||
const loadModule = require('./load-module');
|
const noJS = (a) => a.replace(/.js$/, '');
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
inherits(CloudCmdProto, Emitify);
|
inherits(CloudCmdProto, Emitify);
|
||||||
|
|
||||||
module.exports = new CloudCmdProto(DOM);
|
export const createCloudCmd = ({DOM, Listeners}) => {
|
||||||
|
return new CloudCmdProto({
|
||||||
|
DOM,
|
||||||
|
Listeners,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
load.addErrorListener((e, src) => {
|
load.addErrorListener((e, src) => {
|
||||||
const msg = `file ${src} could not be loaded`;
|
const msg = `file ${src} could not be loaded`;
|
||||||
Images.show.error(msg);
|
Images.show.error(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
function CloudCmdProto(DOM) {
|
function CloudCmdProto({DOM, Listeners}) {
|
||||||
let Listeners;
|
|
||||||
|
|
||||||
const log = (...a) => {
|
|
||||||
if (!isDev )
|
|
||||||
return;
|
|
||||||
|
|
||||||
console.log(...a);
|
|
||||||
};
|
|
||||||
|
|
||||||
Emitify.call(this);
|
Emitify.call(this);
|
||||||
|
|
||||||
const CloudCmd = this;
|
const CloudCmd = this;
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
|
|
||||||
const {
|
const {Storage, Files} = DOM;
|
||||||
Storage,
|
|
||||||
Files,
|
|
||||||
} = DOM;
|
|
||||||
|
|
||||||
this.log = log;
|
this.log = () => {
|
||||||
|
if (!isDev)
|
||||||
|
return;
|
||||||
|
};
|
||||||
this.prefix = '';
|
this.prefix = '';
|
||||||
this.prefixSocket = '';
|
this.prefixSocket = '';
|
||||||
this.prefixURL = '';
|
this.prefixURL = '';
|
||||||
this.DIRCLIENT = '/dist/';
|
|
||||||
this.DIRCLIENT_MODULES = this.DIRCLIENT + 'modules/';
|
|
||||||
|
|
||||||
this.MIN_ONE_PANEL_WIDTH = 1155;
|
this.MIN_ONE_PANEL_WIDTH = DOM.getCSSVar('min-one-panel-width');
|
||||||
this.HOST = location.origin || location.protocol + '//' + location.host;
|
this.HOST = location.origin || location.protocol + '//' + location.host;
|
||||||
|
|
||||||
const TITLE = 'Cloud Commander';
|
|
||||||
this.TITLE = TITLE;
|
|
||||||
|
|
||||||
this.sort = {
|
this.sort = {
|
||||||
left: 'name',
|
left: 'name',
|
||||||
right: 'name',
|
right: 'name',
|
||||||
|
|
@ -79,28 +64,16 @@ function CloudCmdProto(DOM) {
|
||||||
right: 'asc',
|
right: 'asc',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
this.changeDir = async (path, overrides = {}) => {
|
||||||
* Функция привязываеться ко всем ссылкам и
|
|
||||||
* загружает содержимое каталогов
|
|
||||||
*
|
|
||||||
* @param params - {
|
|
||||||
* paramLink - ссылка
|
|
||||||
* needRefresh - необходимость обязательной загрузки данных с сервера
|
|
||||||
* panel
|
|
||||||
* }
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
this.loadDir = async (params) => {
|
|
||||||
const p = params;
|
|
||||||
const refresh = p.isRefresh;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
isRefresh,
|
||||||
panel,
|
panel,
|
||||||
history,
|
history = true,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
} = p;
|
} = overrides;
|
||||||
|
|
||||||
|
const refresh = isRefresh;
|
||||||
let panelChanged;
|
let panelChanged;
|
||||||
|
|
||||||
if (!noCurrent && panel && panel !== Info.panel) {
|
if (!noCurrent && panel && panel !== Info.panel) {
|
||||||
|
|
@ -116,11 +89,12 @@ function CloudCmdProto(DOM) {
|
||||||
Images.show.load(imgPosition, panel);
|
Images.show.load(imgPosition, panel);
|
||||||
|
|
||||||
/* загружаем содержимое каталога */
|
/* загружаем содержимое каталога */
|
||||||
await ajaxLoad(p.path, {
|
await ajaxLoad(addSlashToEnd(path), {
|
||||||
refresh,
|
refresh,
|
||||||
history,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
|
showDotFiles: CloudCmd.config('showDotFiles'),
|
||||||
}, panel);
|
}, panel);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -133,6 +107,8 @@ function CloudCmdProto(DOM) {
|
||||||
CloudCmd.prefix = prefix;
|
CloudCmd.prefix = prefix;
|
||||||
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
||||||
CloudCmd.prefixSocket = config.prefixSocket;
|
CloudCmd.prefixSocket = config.prefixSocket;
|
||||||
|
CloudCmd.DIR_DIST = `${prefix}/dist`;
|
||||||
|
CloudCmd.DIR_MODULES = `${this.DIR_DIST}/modules`;
|
||||||
|
|
||||||
CloudCmd.config = (key) => config[key];
|
CloudCmd.config = (key) => config[key];
|
||||||
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
|
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
|
||||||
|
|
@ -141,7 +117,6 @@ function CloudCmdProto(DOM) {
|
||||||
* should be called from config.js only
|
* should be called from config.js only
|
||||||
* after successful update on server
|
* after successful update on server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (key === 'password')
|
if (key === 'password')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -152,22 +127,14 @@ function CloudCmdProto(DOM) {
|
||||||
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
||||||
|
|
||||||
if (!document.body.scrollIntoViewIfNeeded)
|
if (!document.body.scrollIntoViewIfNeeded)
|
||||||
await load.js(prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js');
|
await load.js(`${CloudCmd.DIR_MODULES}/polyfill.js`);
|
||||||
|
|
||||||
await initModules();
|
await initModules();
|
||||||
await baseInit();
|
await baseInit();
|
||||||
await loadStyle();
|
|
||||||
|
|
||||||
CloudCmd.route(location.hash);
|
CloudCmd.route(location.hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function loadStyle() {
|
|
||||||
const {prefix} = CloudCmd;
|
|
||||||
const name = prefix + '/dist/cloudcmd.common.css';
|
|
||||||
|
|
||||||
await load.css(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.route = (path) => {
|
this.route = (path) => {
|
||||||
const query = path.split('/');
|
const query = path.split('/');
|
||||||
|
|
||||||
|
|
@ -177,12 +144,13 @@ function CloudCmdProto(DOM) {
|
||||||
const [kebabModule] = query;
|
const [kebabModule] = query;
|
||||||
const module = noJS(pascalCase(kebabModule.slice(1)));
|
const module = noJS(pascalCase(kebabModule.slice(1)));
|
||||||
|
|
||||||
const file = query[1];
|
const [, file] = query;
|
||||||
const current = DOM.getCurrentByName(file);
|
const current = DOM.getCurrentByName(file);
|
||||||
|
|
||||||
if (file && !current) {
|
if (file && !current) {
|
||||||
const msg = formatMsg('set current file', file, 'error');
|
const msg = formatMsg('set current file', file, 'error');
|
||||||
CloudCmd.log(msg);
|
CloudCmd.log(msg);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,7 +172,7 @@ function CloudCmdProto(DOM) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const initModules = async () => {
|
const initModules = async () => {
|
||||||
CloudCmd.Key = new CloudCmd.Key();
|
CloudCmd.Key = Key;
|
||||||
CloudCmd.Key.bind();
|
CloudCmd.Key.bind();
|
||||||
|
|
||||||
const [, modules] = await tryToCatch(Files.get, 'modules');
|
const [, modules] = await tryToCatch(Files.get, 'modules');
|
||||||
|
|
@ -254,7 +222,7 @@ function CloudCmdProto(DOM) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const dirPath = DOM.getCurrentDirPath();
|
const dirPath = DOM.getCurrentDirPath();
|
||||||
Listeners = CloudCmd.Listeners;
|
|
||||||
Listeners.init();
|
Listeners.init();
|
||||||
|
|
||||||
const panels = getPanels();
|
const panels = getPanels();
|
||||||
|
|
@ -268,7 +236,7 @@ function CloudCmdProto(DOM) {
|
||||||
const data = await Storage.get(dirPath);
|
const data = await Storage.get(dirPath);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
await Storage.set(dirPath, getJsonFromFileTable());
|
await Storage.setJson(dirPath, getJsonFromFileTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanels() {
|
function getPanels() {
|
||||||
|
|
@ -291,19 +259,15 @@ function CloudCmdProto(DOM) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.refresh = async (options = {}) => {
|
this.refresh = async (options = {}) => {
|
||||||
const {
|
const {panel = Info.panel, currentName} = options;
|
||||||
panel = Info.panel,
|
|
||||||
currentName,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
const path = DOM.getCurrentDirPath(panel);
|
const path = DOM.getCurrentDirPath(panel);
|
||||||
|
|
||||||
const isRefresh = true;
|
const isRefresh = true;
|
||||||
const history = false;
|
const history = false;
|
||||||
const noCurrent = options ? options.noCurrent : false;
|
const noCurrent = options?.noCurrent;
|
||||||
|
|
||||||
await CloudCmd.loadDir({
|
await CloudCmd.changeDir(path, {
|
||||||
path,
|
|
||||||
isRefresh,
|
isRefresh,
|
||||||
history,
|
history,
|
||||||
panel,
|
panel,
|
||||||
|
|
@ -319,22 +283,18 @@ function CloudCmdProto(DOM) {
|
||||||
* @param options
|
* @param options
|
||||||
* { refresh, history } - необходимость обновить данные о каталоге
|
* { refresh, history } - необходимость обновить данные о каталоге
|
||||||
* @param panel
|
* @param panel
|
||||||
* @param callback
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
async function ajaxLoad(path, options = {}, panel) {
|
async function ajaxLoad(path, options = {}, panel) {
|
||||||
const {RESTful} = DOM;
|
const {RESTful} = DOM;
|
||||||
|
|
||||||
CloudCmd.log('reading dir: "' + path + '";');
|
CloudCmd.log(`reading dir: "${path}";`);
|
||||||
|
|
||||||
const dirStorage = CloudCmd.config('dirStorage');
|
const dirStorage = CloudCmd.config('dirStorage');
|
||||||
const json = dirStorage && await Storage.get(path);
|
const json = dirStorage && await Storage.getJson(path);
|
||||||
|
|
||||||
const name = options.currentName || Info.name;
|
const name = options.currentName || Info.name;
|
||||||
const {
|
const {noCurrent, refresh} = options;
|
||||||
noCurrent,
|
|
||||||
refresh,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
if (!refresh && json)
|
if (!refresh && json)
|
||||||
return await createFileTable(json, panel, options);
|
return await createFileTable(json, panel, options);
|
||||||
|
|
@ -351,7 +311,8 @@ function CloudCmdProto(DOM) {
|
||||||
const [, newObj] = await RESTful.read(path + query, 'json');
|
const [, newObj] = await RESTful.read(path + query, 'json');
|
||||||
|
|
||||||
if (!newObj)
|
if (!newObj)
|
||||||
return; // that's OK, error handled by RESTful
|
// that's OK, error handled by RESTful
|
||||||
|
return;
|
||||||
|
|
||||||
options.sort = sort;
|
options.sort = sort;
|
||||||
options.order = order;
|
options.order = order;
|
||||||
|
|
@ -364,28 +325,35 @@ function CloudCmdProto(DOM) {
|
||||||
if (!CloudCmd.config('dirStorage'))
|
if (!CloudCmd.config('dirStorage'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Storage.set(path, newObj);
|
Storage.setJson(path, newObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция строит файловую таблицу
|
* Функция строит файловую таблицу
|
||||||
* @param json - данные о файлах
|
* @param data - данные о файлах
|
||||||
* @param panelParam
|
* @param panelParam
|
||||||
* @param history
|
* @param options - history, noCurrent, showDotFiles
|
||||||
* @param callback
|
|
||||||
*/
|
*/
|
||||||
async function createFileTable(data, panelParam, options) {
|
async function createFileTable(data, panelParam, options) {
|
||||||
const {
|
const {
|
||||||
history,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
|
showDotFiles,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const names = ['file', 'path', 'link', 'pathLink'];
|
const names = [
|
||||||
|
'file',
|
||||||
|
'path',
|
||||||
|
'link',
|
||||||
|
'pathLink',
|
||||||
|
];
|
||||||
|
|
||||||
const [
|
const [error, [
|
||||||
error,
|
file,
|
||||||
[file, path, link, pathLink],
|
path,
|
||||||
] = await tryToCatch(Files.get, names);
|
link,
|
||||||
|
pathLink,
|
||||||
|
]] = await tryToCatch(Files.get, names);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return DOM.Dialog.alert(error.responseText);
|
return DOM.Dialog.alert(error.responseText);
|
||||||
|
|
@ -393,10 +361,7 @@ function CloudCmdProto(DOM) {
|
||||||
const panel = panelParam || DOM.getPanel();
|
const panel = panelParam || DOM.getPanel();
|
||||||
const {prefix} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
const {
|
const {dir, name} = Info;
|
||||||
dir,
|
|
||||||
name,
|
|
||||||
} = Info;
|
|
||||||
|
|
||||||
const {childNodes} = panel;
|
const {childNodes} = panel;
|
||||||
let i = childNodes.length;
|
let i = childNodes.length;
|
||||||
|
|
@ -410,6 +375,7 @@ function CloudCmdProto(DOM) {
|
||||||
data,
|
data,
|
||||||
id: panel.id,
|
id: panel.id,
|
||||||
prefix,
|
prefix,
|
||||||
|
showDotFiles,
|
||||||
template: {
|
template: {
|
||||||
file,
|
file,
|
||||||
path,
|
path,
|
||||||
|
|
@ -450,9 +416,7 @@ function CloudCmdProto(DOM) {
|
||||||
|
|
||||||
const path = parentDirPath;
|
const path = parentDirPath;
|
||||||
|
|
||||||
await CloudCmd.loadDir({
|
await CloudCmd.changeDir(path);
|
||||||
path,
|
|
||||||
});
|
|
||||||
|
|
||||||
const current = DOM.getCurrentByName(dir);
|
const current = DOM.getCurrentByName(dir);
|
||||||
const [first] = DOM.getFiles(panel);
|
const [first] = DOM.getFiles(panel);
|
||||||
|
|
@ -462,4 +426,3 @@ function CloudCmdProto(DOM) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,44 @@
|
||||||
'use strict';
|
import '../css/main.css';
|
||||||
|
import process from 'node:process';
|
||||||
require('../css/main.css');
|
import wraptile from 'wraptile';
|
||||||
require('../css/nojs.css');
|
import load from 'load.js';
|
||||||
require('../css/columns/name-size-date.css');
|
import * as Util from '#common/util';
|
||||||
require('../css/columns/name-size.css');
|
import * as CloudFunc from '#common/cloudfunc';
|
||||||
|
import DOM from '#dom';
|
||||||
const wraptile = require('wraptile');
|
import {registerSW, listenSW} from './sw/register.js';
|
||||||
const load = require('load.js');
|
import {initSortPanel, sortPanel} from './sort.js';
|
||||||
|
import {createCloudCmd} from './client.js';
|
||||||
|
import * as Listeners from './listeners/index.js';
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
const {
|
export default init;
|
||||||
registerSW,
|
|
||||||
listenSW,
|
|
||||||
} = require('./sw/register');
|
|
||||||
|
|
||||||
// prevent additional loading of emitify
|
globalThis.CloudCmd = init;
|
||||||
window.Emitify = require('emitify');
|
|
||||||
|
|
||||||
module.exports = window.CloudCmd = (config) => {
|
async function init(config) {
|
||||||
window.Util = require('../common/util');
|
globalThis.CloudCmd = createCloudCmd({
|
||||||
window.CloudFunc = require('../common/cloudfunc');
|
DOM,
|
||||||
|
Listeners,
|
||||||
|
});
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.Util = Util;
|
||||||
|
globalThis.CloudFunc = CloudFunc;
|
||||||
|
|
||||||
const DOM = require('./dom');
|
await register(config);
|
||||||
|
|
||||||
window.DOM = DOM;
|
|
||||||
window.CloudCmd = require('./client');
|
|
||||||
|
|
||||||
register(config);
|
|
||||||
|
|
||||||
require('./listeners');
|
|
||||||
require('./key');
|
|
||||||
require('./sort');
|
|
||||||
|
|
||||||
|
initSortPanel();
|
||||||
|
globalThis.CloudCmd.sortPanel = sortPanel;
|
||||||
const prefix = getPrefix(config.prefix);
|
const prefix = getPrefix(config.prefix);
|
||||||
|
|
||||||
window.CloudCmd.init(prefix, config);
|
globalThis.CloudCmd.init(prefix, config);
|
||||||
};
|
|
||||||
|
if (globalThis.CloudCmd.config('menu') === 'aleman')
|
||||||
|
setTimeout(() => {
|
||||||
|
import('https://esm.sh/@putout/processor-html');
|
||||||
|
import('https://esm.sh/@putout/bundle@5.5');
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
function getPrefix(prefix) {
|
function getPrefix(prefix) {
|
||||||
if (!prefix)
|
if (!prefix)
|
||||||
|
|
@ -52,7 +54,7 @@ const onUpdateFound = wraptile(async (config) => {
|
||||||
if (isDev)
|
if (isDev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const {DOM} = window;
|
const {DOM} = globalThis;
|
||||||
const prefix = getPrefix(config.prefix);
|
const prefix = getPrefix(config.prefix);
|
||||||
|
|
||||||
await load.js(`${prefix}/dist/cloudcmd.common.js`);
|
await load.js(`${prefix}/dist/cloudcmd.common.js`);
|
||||||
|
|
@ -61,7 +63,7 @@ const onUpdateFound = wraptile(async (config) => {
|
||||||
console.log('cloudcmd: sw: updated');
|
console.log('cloudcmd: sw: updated');
|
||||||
|
|
||||||
DOM.Events.removeAll();
|
DOM.Events.removeAll();
|
||||||
window.CloudCmd(config);
|
globalThis.CloudCmd(config);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function register(config) {
|
async function register(config) {
|
||||||
|
|
@ -70,4 +72,3 @@ async function register(config) {
|
||||||
|
|
||||||
listenSW(sw, 'updatefound', onUpdateFound(config));
|
listenSW(sw, 'updatefound', onUpdateFound(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,24 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd*/
|
/* global CloudCmd*/
|
||||||
|
import * as Storage from '#dom/storage';
|
||||||
|
import tryToPromiseAll from '../../common/try-to-promise-all.js';
|
||||||
|
|
||||||
const tryToPromiseAll = require('../../common/try-to-promise-all');
|
|
||||||
const Storage = require('./storage');
|
|
||||||
const DOM = require('./');
|
|
||||||
|
|
||||||
module.exports = new BufferProto();
|
|
||||||
|
|
||||||
function BufferProto() {
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const CLASS = 'cut-file';
|
const CLASS = 'cut-file';
|
||||||
const COPY = 'copy';
|
const COPY = 'copy';
|
||||||
const CUT = 'cut';
|
const CUT = 'cut';
|
||||||
const Buffer = {
|
|
||||||
cut : callIfEnabled.bind(null, cut),
|
|
||||||
copy : callIfEnabled.bind(null, copy),
|
|
||||||
clear : callIfEnabled.bind(null, clear),
|
|
||||||
paste : callIfEnabled.bind(null, paste),
|
|
||||||
};
|
|
||||||
|
|
||||||
function showMessage(msg) {
|
function showMessage(msg) {
|
||||||
DOM.Dialog.alert(msg);
|
globalThis.DOM.Dialog.alert(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNames() {
|
function getNames() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
const files = DOM.getActiveFiles();
|
const files = DOM.getActiveFiles();
|
||||||
const names = DOM.getFilenames(files);
|
|
||||||
|
|
||||||
return names;
|
return DOM.getFilenames(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCutClass() {
|
function addCutClass() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
const files = DOM.getActiveFiles();
|
const files = DOM.getActiveFiles();
|
||||||
|
|
||||||
for (const element of files) {
|
for (const element of files) {
|
||||||
|
|
@ -40,6 +27,7 @@ function BufferProto() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function rmCutClass() {
|
function rmCutClass() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
const files = DOM.getByClassAll(CLASS);
|
const files = DOM.getByClassAll(CLASS);
|
||||||
|
|
||||||
for (const element of files) {
|
for (const element of files) {
|
||||||
|
|
@ -47,19 +35,19 @@ function BufferProto() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function callIfEnabled(callback) {
|
const checkEnabled = (fn) => () => {
|
||||||
const is = CloudCmd.config('buffer');
|
const is = CloudCmd.config('buffer');
|
||||||
|
|
||||||
if (is)
|
if (is)
|
||||||
return callback();
|
return fn();
|
||||||
|
|
||||||
showMessage('Buffer disabled in config!');
|
showMessage('Buffer disabled in config!');
|
||||||
}
|
};
|
||||||
|
|
||||||
async function readBuffer() {
|
async function readBuffer() {
|
||||||
const [e, cp, ct] = await tryToPromiseAll([
|
const [e, cp, ct] = await tryToPromiseAll([
|
||||||
Storage.get(COPY),
|
Storage.getJson(COPY),
|
||||||
Storage.get(CUT),
|
Storage.getJson(CUT),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -69,7 +57,8 @@ function BufferProto() {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copy() {
|
export const copy = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
const names = getNames();
|
const names = getNames();
|
||||||
const from = Info.dirPath;
|
const from = Info.dirPath;
|
||||||
|
|
||||||
|
|
@ -79,13 +68,14 @@ function BufferProto() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Storage.remove(CUT);
|
await Storage.remove(CUT);
|
||||||
await Storage.set(COPY, {
|
await Storage.setJson(COPY, {
|
||||||
from,
|
from,
|
||||||
names,
|
names,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
async function cut() {
|
export const cut = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
const names = getNames();
|
const names = getNames();
|
||||||
const from = Info.dirPath;
|
const from = Info.dirPath;
|
||||||
|
|
||||||
|
|
@ -96,20 +86,21 @@ function BufferProto() {
|
||||||
|
|
||||||
addCutClass();
|
addCutClass();
|
||||||
|
|
||||||
await Storage.set(CUT, {
|
await Storage.setJson(CUT, {
|
||||||
from,
|
from,
|
||||||
names,
|
names,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
async function clear() {
|
export const clear = checkEnabled(async () => {
|
||||||
await Storage.remove(COPY);
|
await Storage.remove(COPY);
|
||||||
await Storage.remove(CUT);
|
await Storage.remove(CUT);
|
||||||
|
|
||||||
rmCutClass();
|
rmCutClass();
|
||||||
}
|
});
|
||||||
|
|
||||||
async function paste() {
|
export const paste = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
const [error, cp, ct] = await readBuffer();
|
const [error, cp, ct] = await readBuffer();
|
||||||
|
|
||||||
if (error || !cp && !ct)
|
if (error || !cp && !ct)
|
||||||
|
|
@ -130,8 +121,4 @@ function BufferProto() {
|
||||||
});
|
});
|
||||||
|
|
||||||
await clear();
|
await clear();
|
||||||
}
|
});
|
||||||
|
|
||||||
return Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
83
client/dom/cmd.js
Normal file
83
client/dom/cmd.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* global DOM */
|
||||||
|
const SELECTED_FILE = 'selected-file';
|
||||||
|
const Cmd = {
|
||||||
|
getSelectedFiles,
|
||||||
|
isSelected,
|
||||||
|
unselectFile,
|
||||||
|
selectFile,
|
||||||
|
selectAllFiles,
|
||||||
|
toggleSelectedFile,
|
||||||
|
toggleAllSelectedFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selected file check
|
||||||
|
*
|
||||||
|
* @param currentFile
|
||||||
|
*/
|
||||||
|
export function isSelected(currentFile) {
|
||||||
|
if (!currentFile)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return DOM.isContainClass(currentFile, SELECTED_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select current file
|
||||||
|
* @param currentFile
|
||||||
|
*/
|
||||||
|
export function selectFile(currentFile) {
|
||||||
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
|
||||||
|
current.classList.add(SELECTED_FILE);
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unselectFile(currentFile) {
|
||||||
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
|
||||||
|
current.classList.remove(SELECTED_FILE);
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleSelectedFile(currentFile) {
|
||||||
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
const name = DOM.getCurrentName(current);
|
||||||
|
|
||||||
|
if (name === '..')
|
||||||
|
return Cmd;
|
||||||
|
|
||||||
|
current.classList.toggle(SELECTED_FILE);
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleAllSelectedFiles() {
|
||||||
|
DOM
|
||||||
|
.getAllFiles()
|
||||||
|
.map(DOM.toggleSelectedFile);
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectAllFiles() {
|
||||||
|
DOM
|
||||||
|
.getAllFiles()
|
||||||
|
.map(DOM.selectFile);
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unified way to get selected files
|
||||||
|
*
|
||||||
|
* @currentFile
|
||||||
|
*/
|
||||||
|
export function getSelectedFiles() {
|
||||||
|
const panel = DOM.getPanel();
|
||||||
|
const selected = DOM.getByClassAll(SELECTED_FILE, panel);
|
||||||
|
|
||||||
|
return Array.from(selected);
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,16 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import createElement from '@cloudcmd/create-element';
|
||||||
const btoa = require('../../common/btoa');
|
import {getTitle, FS} from '#common/cloudfunc';
|
||||||
const createElement = require('@cloudcmd/create-element');
|
import {encode, decode} from '#common/entity';
|
||||||
|
|
||||||
const {
|
|
||||||
encode,
|
|
||||||
decode,
|
|
||||||
} = require('../../common/entity');
|
|
||||||
|
|
||||||
const {
|
|
||||||
getTitle,
|
|
||||||
FS,
|
|
||||||
} = require('../../common/cloudfunc');
|
|
||||||
|
|
||||||
let Title;
|
let Title;
|
||||||
|
|
||||||
const CURRENT_FILE = 'current-file';
|
const CURRENT_FILE = 'current-file';
|
||||||
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
const encodeNBSP = (a) => a?.replace('\xa0', ' ');
|
||||||
const SPACE = ' ';
|
const decodeNBSP = (a) => a?.replace(' ', '\xa0');
|
||||||
|
|
||||||
module.exports._CURRENT_FILE = CURRENT_FILE;
|
export const _CURRENT_FILE = CURRENT_FILE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set name from current (or param) file
|
* set name from current (or param) file
|
||||||
|
|
@ -30,7 +18,7 @@ module.exports._CURRENT_FILE = CURRENT_FILE;
|
||||||
* @param name
|
* @param name
|
||||||
* @param current
|
* @param current
|
||||||
*/
|
*/
|
||||||
module.exports.setCurrentName = (name, current) => {
|
export const setCurrentName = (name, current) => {
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
const {link} = Info;
|
const {link} = Info;
|
||||||
const {prefix} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
@ -41,7 +29,7 @@ module.exports.setCurrentName = (name, current) => {
|
||||||
link.href = dir + encoded;
|
link.href = dir + encoded;
|
||||||
link.innerHTML = encoded;
|
link.innerHTML = encoded;
|
||||||
|
|
||||||
current.setAttribute('data-name', 'js-file-' + btoa(encodeURI(name)));
|
current.setAttribute('data-name', createNameAttribute(name));
|
||||||
CloudCmd.emit('current-file', current);
|
CloudCmd.emit('current-file', current);
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
|
|
@ -52,28 +40,48 @@ module.exports.setCurrentName = (name, current) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentName = (currentFile) => {
|
export const getCurrentName = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
|
||||||
if (!current)
|
if (!current)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
const link = DOM.getCurrentLink(current);
|
return parseNameAttribute(current.getAttribute('data-name'));
|
||||||
|
|
||||||
if (!link)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return decode(link.title)
|
|
||||||
.replace(NBSP_REG, SPACE);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current direcotory path
|
* Generate a `data-name` attribute for the given filename
|
||||||
|
* @param name The string name to encode
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
|
const createNameAttribute = (name) => {
|
||||||
|
const encoded = btoa(encodeURI(name));
|
||||||
|
return `js-file-${encoded}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a `data-name` attribute string back into the original filename
|
||||||
|
* @param attribute The string we wish to decode
|
||||||
|
*/
|
||||||
|
const parseNameAttribute = (attribute) => {
|
||||||
|
attribute = attribute.replace('js-file-', '');
|
||||||
|
return decodeNBSP(decodeURI(atob(attribute)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const _parseNameAttribute = parseNameAttribute;
|
||||||
|
|
||||||
|
const parseHrefAttribute = (prefix, attribute) => {
|
||||||
|
attribute = attribute.replace(RegExp('^' + prefix + FS), '');
|
||||||
|
return decode(decodeNBSP(attribute));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const _parseHrefAttribute = parseHrefAttribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get current directory path
|
||||||
|
*/
|
||||||
|
export const getCurrentDirPath = (panel = DOM.getPanel()) => {
|
||||||
const path = DOM.getByDataName('js-path', panel);
|
const path = DOM.getByDataName('js-path', panel);
|
||||||
return path.textContent
|
return path.textContent;
|
||||||
.replace(NBSP_REG, SPACE);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -81,36 +89,31 @@ module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
|
||||||
*
|
*
|
||||||
* @param currentFile - current file by default
|
* @param currentFile - current file by default
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentPath = (currentFile) => {
|
export const getCurrentPath = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
const [element] = DOM.getByTag('a', current);
|
const [element] = DOM.getByTag('a', current);
|
||||||
const {prefix} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
const path = element
|
return parseHrefAttribute(prefix, element.getAttribute('href'));
|
||||||
.getAttribute('href')
|
|
||||||
.replace(RegExp('^' + prefix + FS), '')
|
|
||||||
.replace(NBSP_REG, SPACE);
|
|
||||||
|
|
||||||
return decode(path);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current direcotory name
|
* get current directory name
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentDirName = () => {
|
export const getCurrentDirName = () => {
|
||||||
const href = DOM.getCurrentDirPath()
|
const href = DOM
|
||||||
|
.getCurrentDirPath()
|
||||||
.replace(/\/$/, '');
|
.replace(/\/$/, '');
|
||||||
|
|
||||||
const substr = href.substr(href, href.lastIndexOf('/'));
|
const substr = href.substr(href, href.lastIndexOf('/'));
|
||||||
const ret = href.replace(substr + '/', '') || '/';
|
|
||||||
|
|
||||||
return ret;
|
return href.replace(`${substr}/`, '') || '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current direcotory path
|
* get current directory path
|
||||||
*/
|
*/
|
||||||
module.exports.getParentDirPath = (panel) => {
|
export const getParentDirPath = (panel) => {
|
||||||
const path = DOM.getCurrentDirPath(panel);
|
const path = DOM.getCurrentDirPath(panel);
|
||||||
const dirName = DOM.getCurrentDirName() + '/';
|
const dirName = DOM.getCurrentDirName() + '/';
|
||||||
const index = path.lastIndexOf(dirName);
|
const index = path.lastIndexOf(dirName);
|
||||||
|
|
@ -122,9 +125,9 @@ module.exports.getParentDirPath = (panel) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get not current direcotory path
|
* get not current directory path
|
||||||
*/
|
*/
|
||||||
module.exports.getNotCurrentDirPath = () => {
|
export const getNotCurrentDirPath = () => {
|
||||||
const panel = DOM.getPanel({
|
const panel = DOM.getPanel({
|
||||||
active: false,
|
active: false,
|
||||||
});
|
});
|
||||||
|
|
@ -137,20 +140,20 @@ module.exports.getNotCurrentDirPath = () => {
|
||||||
*
|
*
|
||||||
* @currentFile
|
* @currentFile
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentFile = () => {
|
export const getCurrentFile = () => {
|
||||||
return DOM.getByClass(CURRENT_FILE);
|
return DOM.getByClass(CURRENT_FILE);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current file by name
|
* get current file by name
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
|
export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
|
||||||
const dataName = 'js-file-' + btoa(encodeURI(name));
|
const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name)));
|
||||||
return DOM.getByDataName(dataName, panel);
|
return DOM.getByDataName(dataName, panel);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private function thet unset currentfile
|
* private function that unset currentFile
|
||||||
*
|
*
|
||||||
* @currentFile
|
* @currentFile
|
||||||
*/
|
*/
|
||||||
|
|
@ -166,7 +169,7 @@ function unsetCurrentFile(currentFile) {
|
||||||
/**
|
/**
|
||||||
* unified way to set current file
|
* unified way to set current file
|
||||||
*/
|
*/
|
||||||
module.exports.setCurrentFile = (currentFile, options) => {
|
export const setCurrentFile = (currentFile, options) => {
|
||||||
const o = options;
|
const o = options;
|
||||||
const currentFileWas = DOM.getCurrentFile();
|
const currentFileWas = DOM.getCurrentFile();
|
||||||
|
|
||||||
|
|
@ -195,7 +198,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
|
||||||
* but it should be false
|
* but it should be false
|
||||||
* to prevent default behavior
|
* to prevent default behavior
|
||||||
*/
|
*/
|
||||||
if (!o || o.history !== false) {
|
if (!o || o.history) {
|
||||||
const historyPath = path === '/' ? path : FS + path;
|
const historyPath = path === '/' ? path : FS + path;
|
||||||
DOM.setHistory(historyPath, null, historyPath);
|
DOM.setHistory(historyPath, null, historyPath);
|
||||||
}
|
}
|
||||||
|
|
@ -203,6 +206,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
|
||||||
|
|
||||||
/* scrolling to current file */
|
/* scrolling to current file */
|
||||||
const CENTER = true;
|
const CENTER = true;
|
||||||
|
|
||||||
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
||||||
|
|
||||||
CloudCmd.emit('current-file', currentFile);
|
CloudCmd.emit('current-file', currentFile);
|
||||||
|
|
@ -212,7 +216,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setCurrentByName = (name) => {
|
export const setCurrentByName = (name) => {
|
||||||
const current = DOM.getCurrentByName(name);
|
const current = DOM.getCurrentByName(name);
|
||||||
return DOM.setCurrentFile(current);
|
return DOM.setCurrentFile(current);
|
||||||
};
|
};
|
||||||
|
|
@ -223,7 +227,7 @@ this.setCurrentByName = (name) => {
|
||||||
* @param layer - element
|
* @param layer - element
|
||||||
* @param - position {x, y}
|
* @param - position {x, y}
|
||||||
*/
|
*/
|
||||||
module.exports.getCurrentByPosition = ({x, y}) => {
|
export const getCurrentByPosition = ({x, y}) => {
|
||||||
const element = document.elementFromPoint(x, y);
|
const element = document.elementFromPoint(x, y);
|
||||||
|
|
||||||
const getEl = (el) => {
|
const getEl = (el) => {
|
||||||
|
|
@ -255,7 +259,7 @@ module.exports.getCurrentByPosition = ({x, y}) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
module.exports.isCurrentFile = (currentFile) => {
|
export const isCurrentFile = (currentFile) => {
|
||||||
if (!currentFile)
|
if (!currentFile)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -267,8 +271,7 @@ module.exports.isCurrentFile = (currentFile) => {
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
*/
|
*/
|
||||||
|
export const setTitle = (name) => {
|
||||||
module.exports.setTitle = (name) => {
|
|
||||||
if (!Title)
|
if (!Title)
|
||||||
Title = DOM.getByTag('title')[0] || createElement('title', {
|
Title = DOM.getByTag('title')[0] || createElement('title', {
|
||||||
innerHTML: name,
|
innerHTML: name,
|
||||||
|
|
@ -285,20 +288,24 @@ module.exports.setTitle = (name) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
module.exports.isCurrentIsDir = (currentFile) => {
|
export const isCurrentIsDir = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
const path = DOM.getCurrentPath(current);
|
||||||
const fileType = DOM.getCurrentType(current);
|
const fileType = DOM.getCurrentType(current);
|
||||||
|
|
||||||
return /^directory(-link)?/.test(fileType);
|
const isZip = path.endsWith('.zip');
|
||||||
|
const isDir = /^directory(-link)?/.test(fileType);
|
||||||
|
|
||||||
|
return isDir || isZip;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.getCurrentType = (currentFile) => {
|
export const getCurrentType = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
const el = DOM.getByDataName('js-type', current);
|
const el = DOM.getByDataName('js-type', current);
|
||||||
|
|
||||||
const type = el.className
|
const type = el.className
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.pop();
|
.pop();
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,72 @@
|
||||||
'use strict';
|
import {test, stub} from 'supertape';
|
||||||
|
import {create} from 'auto-globals';
|
||||||
|
import wraptile from 'wraptile';
|
||||||
|
import * as currentFile from './current-file.js';
|
||||||
|
|
||||||
const test = require('supertape');
|
|
||||||
const {create} = require('auto-globals');
|
|
||||||
const stub = require('@cloudcmd/stub');
|
|
||||||
const id = (a) => a;
|
const id = (a) => a;
|
||||||
const wraptile = require('wraptile');
|
|
||||||
const returns = wraptile(id);
|
|
||||||
|
|
||||||
const currentFile = require('./current-file');
|
const returns = wraptile(id);
|
||||||
const {_CURRENT_FILE} = currentFile;
|
const {_CURRENT_FILE} = currentFile;
|
||||||
|
|
||||||
test('current-file: setCurrentName: setAttribute', (t) => {
|
test('current-file: setCurrentName: setAttribute', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
const {setAttribute} = current;
|
const {setAttribute} = current;
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
t.ok(setAttribute.calledWith('data-name', 'js-file-aGVsbG8='), 'should call setAttribute');
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
global.DOM = DOM;
|
|
||||||
global.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
|
t.calledWith(setAttribute, [
|
||||||
|
'data-name',
|
||||||
|
'js-file-aGVsbG8=',
|
||||||
|
], 'should call setAttribute');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
|
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
const {setAttribute} = current;
|
const {setAttribute} = current;
|
||||||
|
|
||||||
currentFile.setCurrentName('ай', current);
|
currentFile.setCurrentName('ай', current);
|
||||||
|
|
||||||
t.ok(setAttribute.calledWith('data-name', 'js-file-JUQwJUIwJUQwJUI5'), 'should call setAttribute');
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
global.DOM = DOM;
|
t.calledWith(setAttribute, [
|
||||||
global.CloudCmd = CloudCmd;
|
'data-name',
|
||||||
|
'js-file-JUQwJUIwJUQwJUI5',
|
||||||
|
], 'should call setAttribute');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: getCurrentName', (t) => {
|
||||||
|
const current = create();
|
||||||
|
current.getAttribute.returns('js-file-Ymlu');
|
||||||
|
|
||||||
|
const result = currentFile.getCurrentName(current);
|
||||||
|
|
||||||
|
t.equal(result, 'bin');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: emit', (t) => {
|
test('current-file: emit', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
const emit = stub();
|
const emit = stub();
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd({
|
globalThis.CloudCmd = getCloudCmd({
|
||||||
emit,
|
emit,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -71,47 +74,41 @@ test('current-file: emit', (t) => {
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
t.ok(emit.calledWith('current-file', current), 'should call emit');
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
global.DOM = DOM;
|
|
||||||
global.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
|
t.calledWith(emit, ['current-file', current], 'should call emit');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: setCurrentName: return', (t) => {
|
test('current-file: setCurrentName: return', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
const link = {};
|
const link = {};
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
link,
|
link,
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.setCurrentName('hello', current);
|
const result = currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.equal(result, link, 'should return link');
|
t.equal(result, link, 'should return link');
|
||||||
|
|
||||||
global.DOM = DOM;
|
|
||||||
global.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: getParentDirPath: result', (t) => {
|
test('current-file: getParentDirPath: result', (t) => {
|
||||||
const {DOM} = global;
|
const {DOM} = globalThis;
|
||||||
|
|
||||||
const getCurrentDirPath = returns('/D/Films/+++favorite films/');
|
const getCurrentDirPath = returns('/D/Films/+++favorite films/');
|
||||||
const getCurrentDirName = returns('+++favorite films');
|
const getCurrentDirName = returns('+++favorite films');
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
getCurrentDirName,
|
getCurrentDirName,
|
||||||
});
|
});
|
||||||
|
|
@ -119,65 +116,55 @@ test('current-file: getParentDirPath: result', (t) => {
|
||||||
const result = currentFile.getParentDirPath();
|
const result = currentFile.getParentDirPath();
|
||||||
const expected = '/D/Films/';
|
const expected = '/D/Films/';
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
|
|
||||||
t.equal(result, expected, 'should return parent dir path');
|
t.equal(result, expected, 'should return parent dir path');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentFile: no', (t) => {
|
test('current-file: isCurrentFile: no', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const result = currentFile.isCurrentFile();
|
const result = currentFile.isCurrentFile();
|
||||||
const expect = false;
|
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.equal(result, expect, 'should equal');
|
t.notOk(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentFile', (t) => {
|
test('current-file: isCurrentFile', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
const isContainClass = stub();
|
const isContainClass = stub();
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
isContainClass,
|
isContainClass,
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
currentFile.isCurrentFile(current);
|
currentFile.isCurrentFile(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.ok(isContainClass.calledWith(current, _CURRENT_FILE), 'should call isContainClass');
|
t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: getCurrentType', (t) => {
|
test('current-file: getCurrentType', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const {getByDataName} = global.DOM;
|
const {getByDataName} = globalThis.DOM;
|
||||||
|
|
||||||
getByDataName.returns({
|
getByDataName.returns({
|
||||||
className: 'mini-icon directory',
|
className: 'mini-icon directory',
|
||||||
|
|
@ -187,122 +174,129 @@ test('current-file: getCurrentType', (t) => {
|
||||||
|
|
||||||
currentFile.getCurrentType(current);
|
currentFile.getCurrentType(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.ok(getByDataName.calledWith('js-type', current));
|
t.calledWith(getByDataName, ['js-type', current]);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
|
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const {getCurrentType} = global.DOM;
|
const {getCurrentType} = globalThis.DOM;
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
currentFile.isCurrentIsDir(current);
|
currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.ok(getCurrentType.calledWith(current));
|
t.calledWith(getCurrentType, [current]);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: directory', (t) => {
|
test('current-file: isCurrentIsDir: directory', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('directory'),
|
getCurrentType: stub().returns('directory'),
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.ok(result);
|
t.ok(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: directory-link', (t) => {
|
test('current-file: isCurrentIsDir: directory-link', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('directory-link'),
|
getCurrentType: stub().returns('directory-link'),
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.ok(result);
|
t.ok(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: file', (t) => {
|
test('current-file: isCurrentIsDir: file', (t) => {
|
||||||
const {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
DOM,
|
|
||||||
CloudCmd,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('file'),
|
getCurrentType: stub().returns('file'),
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
global.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
global.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.notOk(result);
|
t.notOk(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
function getCloudCmd({emit} = {}) {
|
const getCloudCmd = ({emit} = {}) => ({
|
||||||
return {
|
|
||||||
prefix: '',
|
prefix: '',
|
||||||
emit: emit || stub(),
|
emit: emit || stub(),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function getDOM({
|
test('current-file: parseNameAttribute', (t) => {
|
||||||
|
const result = currentFile._parseNameAttribute('js-file-aGVsbG8mbmJzcDt3b3JsZA==');
|
||||||
|
const expected = 'hello\xa0world';
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: parseHrefAttribute', (t) => {
|
||||||
|
const prefix = '/api/v1';
|
||||||
|
const result = currentFile._parseHrefAttribute(prefix, '/api/v1/fs/hello world');
|
||||||
|
const expected = '/hello\xa0world';
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getDOM(overrides = {}) {
|
||||||
|
const {
|
||||||
link = {},
|
link = {},
|
||||||
getCurrentDirPath = stub(),
|
getCurrentDirPath = stub(),
|
||||||
getCurrentDirName = stub(),
|
getCurrentDirName = stub(),
|
||||||
getByDataName = stub(),
|
getByDataName = stub(),
|
||||||
isContainClass = stub(),
|
isContainClass = stub(),
|
||||||
getCurrentType = stub(),
|
getCurrentType = stub(),
|
||||||
} = {}) {
|
getCurrentPath = stub().returns(''),
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
getCurrentDirName,
|
getCurrentDirName,
|
||||||
|
getCurrentPath,
|
||||||
getByDataName,
|
getByDataName,
|
||||||
isContainClass,
|
isContainClass,
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
|
|
@ -312,4 +306,3 @@ function getDOM({
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,18 @@
|
||||||
'use strict';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import * as smalltalk from 'smalltalk';
|
||||||
const tryToCatch = require('try-to-catch');
|
|
||||||
|
|
||||||
const {
|
|
||||||
alert,
|
|
||||||
prompt,
|
|
||||||
confirm,
|
|
||||||
progress,
|
|
||||||
} = require('smalltalk');
|
|
||||||
|
|
||||||
const title = 'Cloud Commander';
|
const title = 'Cloud Commander';
|
||||||
|
|
||||||
module.exports.alert = (...a) => alert(title, ...a, {
|
export const alert = (...a) => smalltalk.alert(title, ...a, {
|
||||||
cancel: false,
|
cancel: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a);
|
export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a);
|
||||||
module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a);
|
export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a);
|
||||||
module.exports.progress = (...a) => progress(title, ...a);
|
export const progress = (...a) => smalltalk.progress(title, ...a);
|
||||||
|
|
||||||
module.exports.alert.noFiles = () => {
|
alert.noFiles = () => {
|
||||||
return alert(title, 'No files selected!', {
|
return smalltalk.alert(title, 'No files selected!', {
|
||||||
cancel: false,
|
cancel: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,19 @@
|
||||||
'use strict';
|
/* global DOM, CloudCmd */
|
||||||
|
import philip from 'philip';
|
||||||
|
import * as Dialog from '#dom/dialog';
|
||||||
|
import {FS} from '#common/cloudfunc';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
|
||||||
/* global CloudCmd */
|
export const uploadDirectory = (items) => {
|
||||||
|
|
||||||
const philip = require('philip');
|
|
||||||
|
|
||||||
const Images = require('./images');
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
const DOM = require('.');
|
|
||||||
const Dialog = require('./dialog');
|
|
||||||
|
|
||||||
const {getCurrentDirPath: getPathWhenRootEmpty} = DOM;
|
|
||||||
|
|
||||||
module.exports = (items) => {
|
|
||||||
if (items.length)
|
if (items.length)
|
||||||
Images.show('top');
|
Images.show('top');
|
||||||
|
|
||||||
const entries = Array.from(items).map((item) => {
|
const entries = Array
|
||||||
return item.webkitGetAsEntry();
|
.from(items)
|
||||||
});
|
.map((item) => item.webkitGetAsEntry());
|
||||||
|
|
||||||
const dirPath = getPathWhenRootEmpty();
|
const dirPath = DOM.getCurrentDirPath();
|
||||||
const path = dirPath
|
const path = dirPath.replace(/\/$/, '');
|
||||||
.replace(/\/$/, '');
|
|
||||||
|
|
||||||
const progress = Dialog.progress('Uploading...');
|
const progress = Dialog.progress('Uploading...');
|
||||||
|
|
||||||
|
|
@ -65,15 +57,8 @@ module.exports = (items) => {
|
||||||
uploader.on('end', CloudCmd.refresh);
|
uploader.on('end', CloudCmd.refresh);
|
||||||
};
|
};
|
||||||
|
|
||||||
function percent(i, n, per = 100) {
|
const percent = (i, n, per = 100) => Math.round(i * per / n);
|
||||||
return Math.round(i * per / n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile(url, data) {
|
const uploadFile = (url, data) => DOM.load.put(url, data);
|
||||||
return DOM.load.put(url, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadDir(url) {
|
|
||||||
return DOM.load.put(url + '?dir');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const uploadDir = (url) => DOM.load.put(`${url}?dir`);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
'use strict';
|
import currify from 'currify';
|
||||||
|
|
||||||
const currify = require('currify');
|
const DOM = {
|
||||||
|
show,
|
||||||
const DOM = module.exports;
|
hide,
|
||||||
|
getByClass,
|
||||||
|
getByClassAll,
|
||||||
|
getByDataName,
|
||||||
|
getById,
|
||||||
|
getByTag,
|
||||||
|
isContainClass,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check class of element
|
* check class of element
|
||||||
|
|
@ -10,7 +17,7 @@ const DOM = module.exports;
|
||||||
* @param element
|
* @param element
|
||||||
* @param className
|
* @param className
|
||||||
*/
|
*/
|
||||||
const isContainClass = (element, className) => {
|
export function isContainClass(element, className) {
|
||||||
if (!element)
|
if (!element)
|
||||||
throw Error('element could not be empty!');
|
throw Error('element could not be empty!');
|
||||||
|
|
||||||
|
|
@ -18,67 +25,68 @@ const isContainClass = (element, className) => {
|
||||||
throw Error('className could not be empty!');
|
throw Error('className could not be empty!');
|
||||||
|
|
||||||
if (Array.isArray(className))
|
if (Array.isArray(className))
|
||||||
return className.some(currify(isContainClass, element));
|
return className.some(currify(
|
||||||
|
isContainClass,
|
||||||
|
element,
|
||||||
|
));
|
||||||
|
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
|
|
||||||
return classList.contains(className);
|
return classList.contains(className);
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.isContainClass = isContainClass;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by tag
|
* Function search element by tag
|
||||||
* @param tag - className
|
* @param tag - className
|
||||||
* @param element - element
|
* @param element - element
|
||||||
*/
|
*/
|
||||||
module.exports.getByTag = (tag, element = document) => {
|
export function getByTag(tag, element = document) {
|
||||||
return element.getElementsByTagName(tag);
|
return element.getElementsByTagName(tag);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by id
|
* Function search element by id
|
||||||
* @param Id - id
|
* @param id
|
||||||
|
* @param element
|
||||||
*/
|
*/
|
||||||
module.exports.getById = (id, element = document) => {
|
export function getById(id, element = document) {
|
||||||
return element.querySelector('#' + id);
|
return element.querySelector(`#${id}`);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search first element by class name
|
* Function search first element by class name
|
||||||
* @param className - className
|
* @param className - className
|
||||||
* @param element - element
|
* @param element - element
|
||||||
*/
|
*/
|
||||||
module.exports.getByClass = (className, element = document) => {
|
export function getByClass(className, element = document) {
|
||||||
return DOM.getByClassAll(className, element)[0];
|
return DOM.getByClassAll(className, element)[0];
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.getByDataName = (attribute, element = document) => {
|
export function getByDataName(attribute, element = document) {
|
||||||
const selector = '[' + 'data-name="' + attribute + '"]';
|
const selector = '[' + 'data-name="' + attribute + '"]';
|
||||||
return element.querySelector(selector);
|
return element.querySelector(selector);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by class name
|
* Function search element by class name
|
||||||
* @param pClass - className
|
* @param className
|
||||||
* @param element - element
|
* @param element
|
||||||
*/
|
*/
|
||||||
module.exports.getByClassAll = (className, element) => {
|
export function getByClassAll(className, element) {
|
||||||
return (element || document).getElementsByClassName(className);
|
return (element || document).getElementsByClassName(className);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add class=hidden to element
|
* add class=hidden to element
|
||||||
*
|
*
|
||||||
* @param element
|
* @param element
|
||||||
*/
|
*/
|
||||||
module.exports.hide = (element) => {
|
export function hide(element) {
|
||||||
element.classList.add('hidden');
|
element.classList.add('hidden');
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.show = (element) => {
|
export function show(element) {
|
||||||
element.classList.remove('hidden');
|
element.classList.remove('hidden');
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
'use strict';
|
import {test, stub} from 'supertape';
|
||||||
|
import {create} from 'auto-globals';
|
||||||
const test = require('supertape');
|
import {tryCatch} from 'try-catch';
|
||||||
const {create} = require('auto-globals');
|
import {
|
||||||
const tryCatch = require('try-catch');
|
isContainClass,
|
||||||
|
getByTag,
|
||||||
const {isContainClass} = require('./dom-tree');
|
getById,
|
||||||
|
getByClass,
|
||||||
|
getByDataName,
|
||||||
|
getByClassAll,
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
} from './dom-tree.js';
|
||||||
|
|
||||||
test('dom: isContainClass: no element', (t) => {
|
test('dom: isContainClass: no element', (t) => {
|
||||||
const [e] = tryCatch(isContainClass);
|
const [e] = tryCatch(isContainClass);
|
||||||
|
|
||||||
t.equal(e.message, 'element could not be empty!', 'should throw when no element');
|
t.equal(e.message, 'element could not be empty!', 'should throw when no element');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dom: isContainClass: no className', (t) => {
|
test('dom: isContainClass: no className', (t) => {
|
||||||
const [e] = tryCatch(isContainClass, {});
|
const [e] = tryCatch(isContainClass, {});
|
||||||
|
|
||||||
t.equal(e.message, 'className could not be empty!', 'should throw when no element');
|
t.equal(e.message, 'className could not be empty!', 'should throw when no element');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
@ -25,7 +33,7 @@ test('dom: isContainClass: contains', (t) => {
|
||||||
const className = 'hello';
|
const className = 'hello';
|
||||||
isContainClass(el, className);
|
isContainClass(el, className);
|
||||||
|
|
||||||
t.ok(contains.calledWith(className), 'should call contains');
|
t.calledWith(contains, [className], 'should call contains');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -34,13 +42,111 @@ test('dom: isContainClass: contains: array', (t) => {
|
||||||
const {contains} = el.classList;
|
const {contains} = el.classList;
|
||||||
|
|
||||||
const className = 'hello';
|
const className = 'hello';
|
||||||
isContainClass(el, [
|
isContainClass(el, ['world', className, 'hello']);
|
||||||
'world',
|
|
||||||
className,
|
|
||||||
'hello',
|
|
||||||
]);
|
|
||||||
|
|
||||||
t.ok(contains.calledWith(className), 'should call contains');
|
t.calledWith(contains, [className], 'should call contains');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('dom: getByTag', (t) => {
|
||||||
|
const getElementsByTagName = stub();
|
||||||
|
const element = {
|
||||||
|
getElementsByTagName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByTag('div', element);
|
||||||
|
|
||||||
|
t.calledWith(getElementsByTagName, ['div'], 'should call getElementsByTagName');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getById', (t) => {
|
||||||
|
const querySelector = stub();
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
getById('my-id', element);
|
||||||
|
|
||||||
|
t.calledWith(querySelector, ['#my-id'], 'should call querySelector with id selector');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClassAll', (t) => {
|
||||||
|
const getElementsByClassName = stub();
|
||||||
|
const element = {
|
||||||
|
getElementsByClassName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByClassAll('my-class', element);
|
||||||
|
|
||||||
|
t.calledWith(getElementsByClassName, ['my-class'], 'should call getElementsByClassName');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClass: calls getByClassAll', (t) => {
|
||||||
|
const element = {
|
||||||
|
getElementsByClassName: stub().returns(['first']),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getByClass('my-class', element);
|
||||||
|
|
||||||
|
t.equal(result, 'first', 'should return first element from class list');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByDataName', (t) => {
|
||||||
|
const querySelector = stub();
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByDataName('hello', element);
|
||||||
|
|
||||||
|
t.calledWith(querySelector, ['[data-name="hello"]'], 'should call querySelector with data-name selector');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: hide', (t) => {
|
||||||
|
const add = stub();
|
||||||
|
const element = {
|
||||||
|
classList: {
|
||||||
|
add,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
hide(element);
|
||||||
|
|
||||||
|
t.calledWith(add, ['hidden'], 'should add hidden class');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: show', (t) => {
|
||||||
|
const remove = stub();
|
||||||
|
const element = {
|
||||||
|
classList: {
|
||||||
|
remove,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
show(element);
|
||||||
|
|
||||||
|
t.calledWith(remove, ['hidden'], 'should remove hidden class');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClassAll: without element uses document', (t) => {
|
||||||
|
const getElementsByClassName = stub();
|
||||||
|
const prevDocument = globalThis.document;
|
||||||
|
|
||||||
|
globalThis.document = {
|
||||||
|
getElementsByClassName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByClassAll('my-class');
|
||||||
|
|
||||||
|
globalThis.document = prevDocument;
|
||||||
|
|
||||||
|
t.calledWith(getElementsByClassName, ['my-class'], 'should fallback to document when no element');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
let list = [];
|
let list = [];
|
||||||
|
|
||||||
module.exports.add = (el, name, fn) => {
|
export const add = (el, name, fn) => {
|
||||||
list.push([
|
list.push([
|
||||||
el,
|
el,
|
||||||
name,
|
name,
|
||||||
|
|
@ -10,11 +8,8 @@ module.exports.add = (el, name, fn) => {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.clear = () => {
|
export const clear = () => {
|
||||||
list = [];
|
list = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.get = () => {
|
export const get = () => list;
|
||||||
return list;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
'use strict';
|
import {test} from 'supertape';
|
||||||
|
import * as eventStore from './event-store.js';
|
||||||
const test = require('supertape');
|
|
||||||
const eventStore = require('./event-store');
|
|
||||||
|
|
||||||
test('event-store: get', (t) => {
|
test('event-store: get', (t) => {
|
||||||
const el = {};
|
const el = {};
|
||||||
|
|
@ -10,11 +8,16 @@ test('event-store: get', (t) => {
|
||||||
|
|
||||||
eventStore.add(el, name, fn);
|
eventStore.add(el, name, fn);
|
||||||
const result = eventStore.get();
|
const result = eventStore.get();
|
||||||
|
|
||||||
const expected = [
|
const expected = [
|
||||||
[el, name, fn],
|
[
|
||||||
|
el,
|
||||||
|
name,
|
||||||
|
fn,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
t.deepEqual(expected, result, 'should equal');
|
t.deepEqual(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -29,6 +32,6 @@ test('event-store: clear', (t) => {
|
||||||
const result = eventStore.get();
|
const result = eventStore.get();
|
||||||
const expected = [];
|
const expected = [];
|
||||||
|
|
||||||
t.deepEqual(expected, result, 'should equal');
|
t.deepEqual(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,125 @@
|
||||||
'use strict';
|
import itype from 'itype';
|
||||||
|
import * as EventStore from './event-store.js';
|
||||||
|
|
||||||
const itype = require('itype');
|
/**
|
||||||
const EventStore = require('./event-store');
|
* safe add event listener
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param element - document by default
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
export const add = (type, element, listener) => {
|
||||||
|
checkType(type);
|
||||||
|
|
||||||
module.exports = new EventsProto();
|
parseArgs(type, element, listener, (element, args) => {
|
||||||
|
const [name, fn, options] = args;
|
||||||
|
|
||||||
function EventsProto() {
|
element.addEventListener(name, fn, options);
|
||||||
const Events = this;
|
EventStore.add(element, name, fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Events;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event listener
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param listener
|
||||||
|
* @param element - document by default
|
||||||
|
*/
|
||||||
|
export const addOnce = (type, element, listener) => {
|
||||||
|
const once = (event) => {
|
||||||
|
Events.remove(type, element, once);
|
||||||
|
listener(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!listener) {
|
||||||
|
listener = element;
|
||||||
|
element = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(type, element, once);
|
||||||
|
|
||||||
|
return Events;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe remove event listener
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param listener
|
||||||
|
* @param element - document by default
|
||||||
|
*/
|
||||||
|
export const remove = (type, element, listener) => {
|
||||||
|
checkType(type);
|
||||||
|
|
||||||
|
parseArgs(type, element, listener, (element, args) => {
|
||||||
|
element.removeEventListener(...args);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Events;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove all added event listeners
|
||||||
|
*/
|
||||||
|
export const removeAll = () => {
|
||||||
|
const events = EventStore.get();
|
||||||
|
|
||||||
|
for (const [el, name, fn] of events)
|
||||||
|
el.removeEventListener(name, fn);
|
||||||
|
|
||||||
|
EventStore.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event keydown listener
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
export const addKey = function(...args) {
|
||||||
|
return add('keydown', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe remove event click listener
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
export const rmKey = function(...args) {
|
||||||
|
return Events.remove('keydown', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event click listener
|
||||||
|
*/
|
||||||
|
export const addClick = function(...args) {
|
||||||
|
return Events.add('click', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe remove event click listener
|
||||||
|
*/
|
||||||
|
export const rmClick = function(...args) {
|
||||||
|
return remove('click', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addContextMenu = function(...args) {
|
||||||
|
return add('contextmenu', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add load listener
|
||||||
|
*/
|
||||||
|
export const addLoad = function(...args) {
|
||||||
|
return add('load', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkType(type) {
|
||||||
|
if (!type)
|
||||||
|
throw Error('type could not be empty!');
|
||||||
|
}
|
||||||
|
|
||||||
const getEventOptions = (eventName) => {
|
const getEventOptions = (eventName) => {
|
||||||
if (eventName !== 'touchstart')
|
if (eventName !== 'touchstart')
|
||||||
|
|
@ -32,15 +145,10 @@ function EventsProto() {
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
default:
|
default:
|
||||||
if (!/element$/.test(type))
|
if (!type.endsWith('element'))
|
||||||
throw Error('unknown eventName: ' + type);
|
throw Error(`unknown eventName: ${type}`);
|
||||||
|
|
||||||
parseArgs(
|
parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
|
||||||
args[EVENT_NAME],
|
|
||||||
args[ELEMENT],
|
|
||||||
listener,
|
|
||||||
callback,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'string':
|
case 'string':
|
||||||
|
|
@ -62,189 +170,34 @@ function EventsProto() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'array':
|
case 'array':
|
||||||
|
|
||||||
for (const name of eventName) {
|
for (const name of eventName) {
|
||||||
parseArgs(
|
parseArgs(name, element, listener, callback);
|
||||||
name,
|
|
||||||
element,
|
|
||||||
listener,
|
|
||||||
callback,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'object':
|
case 'object':
|
||||||
|
|
||||||
for (const name of Object.keys(eventName)) {
|
for (const name of Object.keys(eventName)) {
|
||||||
const eventListener = eventName[name];
|
const eventListener = eventName[name];
|
||||||
|
|
||||||
parseArgs(
|
parseArgs(name, element, eventListener, callback);
|
||||||
name,
|
|
||||||
element,
|
|
||||||
eventListener,
|
|
||||||
callback,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const Events = {
|
||||||
* safe add event listener
|
add,
|
||||||
*
|
addClick,
|
||||||
* @param type
|
addContextMenu,
|
||||||
* @param element {document by default}
|
addKey,
|
||||||
* @param listener
|
addLoad,
|
||||||
*/
|
addOnce,
|
||||||
this.add = (type, element, listener) => {
|
remove,
|
||||||
checkType(type);
|
removeAll,
|
||||||
|
rmClick,
|
||||||
parseArgs(type, element, listener, (element, args) => {
|
rmKey,
|
||||||
const [name, fn, options] = args;
|
|
||||||
|
|
||||||
element.addEventListener(name, fn, options);
|
|
||||||
EventStore.add(element, name, fn);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Events;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add event listener
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* @param listener
|
|
||||||
* @param element {document by default}
|
|
||||||
*/
|
|
||||||
this.addOnce = (type, element, listener) => {
|
|
||||||
const once = (event) => {
|
|
||||||
Events.remove(type, element, once);
|
|
||||||
listener(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!listener) {
|
|
||||||
listener = element;
|
|
||||||
element = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.add(type, element, once);
|
|
||||||
|
|
||||||
return Events;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe remove event listener
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* @param listener
|
|
||||||
* @param element {document by default}
|
|
||||||
*/
|
|
||||||
this.remove = (type, element, listener) => {
|
|
||||||
checkType(type);
|
|
||||||
|
|
||||||
parseArgs(type, element, listener, (element, args) => {
|
|
||||||
element.removeEventListener(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Events;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* remove all added event listeners
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.removeAll = () => {
|
|
||||||
const events = EventStore.get();
|
|
||||||
|
|
||||||
for (const [el, name, fn] of events)
|
|
||||||
el.removeEventListener(name, fn);
|
|
||||||
|
|
||||||
EventStore.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add event keydown listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.addKey = function(...argsArr) {
|
|
||||||
const name = 'keydown';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.add(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe remove event click listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.rmKey = function(...argsArr) {
|
|
||||||
const name = 'keydown';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.remove(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add event click listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.addClick = function(...argsArr) {
|
|
||||||
const name = 'click';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.add(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe remove event click listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.rmClick = function(...argsArr) {
|
|
||||||
const name = 'click';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.remove(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addContextMenu = function(...argsArr) {
|
|
||||||
const name = 'contextmenu';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.add(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add event click listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.addError = function(...argsArr) {
|
|
||||||
const name = 'error';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.add(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add load click listener
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.addLoad = function(...argsArr) {
|
|
||||||
const name = 'load';
|
|
||||||
const args = [name, ...argsArr];
|
|
||||||
|
|
||||||
return Events.add(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
function checkType(type) {
|
|
||||||
if (!type)
|
|
||||||
throw Error('type could not be empty!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,29 @@
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import itype from 'itype';
|
||||||
'use strict';
|
import {promisify} from 'es6-promisify';
|
||||||
|
import * as load from '#dom/load';
|
||||||
const itype = require('itype');
|
import * as RESTful from '#dom/rest';
|
||||||
const currify = require('currify');
|
|
||||||
const {promisify} = require('es6-promisify');
|
|
||||||
|
|
||||||
const load = require('./load');
|
|
||||||
const RESTful = require('./rest');
|
|
||||||
|
|
||||||
const Promises = {};
|
const Promises = {};
|
||||||
const FILES_JSON = 'config|modules';
|
const FILES_JSON = 'config|modules';
|
||||||
const FILES_HTML = 'file|path|link|pathLink|media';
|
const FILES_HTML = 'file|path|link|pathLink|media';
|
||||||
const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload';
|
const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload';
|
||||||
const DIR_HTML = '/tmpl/';
|
const DIR_HTML = '/tmpl/';
|
||||||
const DIR_HTML_FS = DIR_HTML + 'fs/';
|
const DIR_HTML_FS = `${DIR_HTML}fs/`;
|
||||||
const DIR_JSON = '/json/';
|
const DIR_JSON = '/json/';
|
||||||
const timeout = getTimeoutOnce(2000);
|
const timeout = getTimeoutOnce(2000);
|
||||||
|
|
||||||
const get = currify(getFile);
|
export const get = getFile;
|
||||||
const unaryMap = (array, fn) => array.map((a) => fn(a));
|
|
||||||
|
|
||||||
module.exports.get = get;
|
function getFile(name) {
|
||||||
|
|
||||||
async function getFile(name) {
|
|
||||||
const type = itype(name);
|
const type = itype(name);
|
||||||
check(name);
|
check(name);
|
||||||
|
|
||||||
if (type === 'string')
|
if (type === 'string')
|
||||||
return await getModule(name);
|
return getModule(name);
|
||||||
|
|
||||||
if (type === 'array')
|
if (type === 'array')
|
||||||
return Promise.all(unaryMap(name, get));
|
return Promise.all(name.map(getFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
function check(name) {
|
function check(name) {
|
||||||
|
|
@ -39,9 +31,9 @@ function check(name) {
|
||||||
throw Error('name could not be empty!');
|
throw Error('name could not be empty!');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getModule(name) {
|
function getModule(name) {
|
||||||
const regExpHTML = new RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
const regExpHTML = RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
||||||
const regExpJSON = new RegExp(FILES_JSON);
|
const regExpJSON = RegExp(FILES_JSON);
|
||||||
|
|
||||||
const isHTML = regExpHTML.test(name);
|
const isHTML = regExpHTML.test(name);
|
||||||
const isJSON = regExpJSON.test(name);
|
const isJSON = regExpJSON.test(name);
|
||||||
|
|
@ -53,12 +45,13 @@ async function getModule(name) {
|
||||||
return getConfig();
|
return getConfig();
|
||||||
|
|
||||||
const path = getPath(name, isHTML, isJSON);
|
const path = getPath(name, isHTML, isJSON);
|
||||||
|
|
||||||
return getSystemFile(path);
|
return getSystemFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPath(name, isHTML, isJSON) {
|
function getPath(name, isHTML, isJSON) {
|
||||||
let path;
|
let path;
|
||||||
const regExp = new RegExp(FILES_HTML_ROOT);
|
const regExp = RegExp(FILES_HTML_ROOT);
|
||||||
const isRoot = regExp.test(name);
|
const isRoot = regExp.test(name);
|
||||||
|
|
||||||
if (isHTML) {
|
if (isHTML) {
|
||||||
|
|
@ -76,8 +69,8 @@ function getPath(name, isHTML, isJSON) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(name) {
|
function showError(name) {
|
||||||
const str = 'Wrong file name: ' + name;
|
const str = `Wrong file name: ${name}`;
|
||||||
const error = new Error(str);
|
const error = Error(str);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -141,4 +134,3 @@ function getTimeoutOnce(time) {
|
||||||
}, time);
|
}, time);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,13 @@
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
import createElement from '@cloudcmd/create-element';
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const createElement = require('@cloudcmd/create-element');
|
|
||||||
|
|
||||||
const Images = module.exports;
|
|
||||||
|
|
||||||
const LOADING = 'loading';
|
const LOADING = 'loading';
|
||||||
const HIDDEN = 'hidden';
|
const HIDDEN = 'hidden';
|
||||||
const ERROR = 'error';
|
const ERROR = 'error';
|
||||||
|
|
||||||
function getLoadingType() {
|
const getLoadingType = () => isSVG() ? '-svg' : '-gif';
|
||||||
return isSVG() ? '-svg' : '-gif';
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.get = getElement;
|
export const get = getElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check SVG SMIL animation support
|
* check SVG SMIL animation support
|
||||||
|
|
@ -43,7 +36,7 @@ function getElement() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Функция создаёт картинку загрузки */
|
/* Функция создаёт картинку загрузки */
|
||||||
module.exports.loading = () => {
|
export const loading = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
const loadingImage = LOADING + getLoadingType();
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
@ -55,7 +48,7 @@ module.exports.loading = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Функция создаёт картинку ошибки загрузки */
|
/* Функция создаёт картинку ошибки загрузки */
|
||||||
module.exports.error = () => {
|
export const error = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
const loadingImage = LOADING + getLoadingType();
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
@ -66,15 +59,21 @@ module.exports.error = () => {
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.show = show;
|
show.load = show;
|
||||||
module.exports.show.load = show;
|
show.error = (text) => {
|
||||||
module.exports.show.error = error;
|
const image = Images.error();
|
||||||
|
|
||||||
|
DOM.show(image);
|
||||||
|
image.title = text;
|
||||||
|
|
||||||
|
return image;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function shows loading spinner
|
* Function shows loading spinner
|
||||||
* position = {top: true};
|
* position = {top: true};
|
||||||
*/
|
*/
|
||||||
function show(position, panel) {
|
export function show(position, panel) {
|
||||||
const image = Images.loading();
|
const image = Images.loading();
|
||||||
const parent = image.parentElement;
|
const parent = image.parentElement;
|
||||||
const refreshButton = DOM.getRefreshButton(panel);
|
const refreshButton = DOM.getRefreshButton(panel);
|
||||||
|
|
@ -100,34 +99,25 @@ function show(position, panel) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(text) {
|
|
||||||
const image = Images.error();
|
|
||||||
|
|
||||||
DOM.show(image);
|
|
||||||
image.title = text;
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hide load image
|
* hide load image
|
||||||
*/
|
*/
|
||||||
module.exports.hide = () => {
|
export function hide() {
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
DOM.hide(element);
|
DOM.hide(element);
|
||||||
|
|
||||||
return Images;
|
return Images;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.setProgress = (value, title) => {
|
export const setProgress = (value, title) => {
|
||||||
const DATA = 'data-progress';
|
const DATA = 'data-progress';
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
if (!element)
|
if (!element)
|
||||||
return Images;
|
return Images;
|
||||||
|
|
||||||
element.setAttribute(DATA, value + '%');
|
element.setAttribute(DATA, `${value}%`);
|
||||||
|
|
||||||
if (title)
|
if (title)
|
||||||
element.title = title;
|
element.title = title;
|
||||||
|
|
@ -135,7 +125,7 @@ module.exports.setProgress = (value, title) => {
|
||||||
return Images;
|
return Images;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.clearProgress = () => {
|
export const clearProgress = () => {
|
||||||
const DATA = 'data-progress';
|
const DATA = 'data-progress';
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
|
|
@ -148,3 +138,12 @@ module.exports.clearProgress = () => {
|
||||||
return Images;
|
return Images;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Images = {
|
||||||
|
clearProgress,
|
||||||
|
setProgress,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
get,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
52
client/dom/index.spec.js
Normal file
52
client/dom/index.spec.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import {getCSSVar, goToDirectory} from '#dom';
|
||||||
|
|
||||||
|
globalThis.CloudCmd = {};
|
||||||
|
|
||||||
|
test('cloudcmd: client: dom: goToDirectory', async (t) => {
|
||||||
|
const path = '';
|
||||||
|
const changeDir = stub();
|
||||||
|
const prompt = stub().returns([null, path]);
|
||||||
|
|
||||||
|
await goToDirectory({
|
||||||
|
prompt,
|
||||||
|
changeDir,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(changeDir, [path]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: dom: getCSSVar', (t) => {
|
||||||
|
const body = {};
|
||||||
|
const getPropertyValue = stub().returns(0);
|
||||||
|
|
||||||
|
globalThis.getComputedStyle = stub().returns({
|
||||||
|
getPropertyValue,
|
||||||
|
});
|
||||||
|
const result = getCSSVar('hello', {
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
delete globalThis.getComputedStyle;
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: dom: getCSSVar: 1', (t) => {
|
||||||
|
const body = {};
|
||||||
|
const getPropertyValue = stub().returns(1);
|
||||||
|
|
||||||
|
globalThis.getComputedStyle = stub().returns({
|
||||||
|
getPropertyValue,
|
||||||
|
});
|
||||||
|
const result = getCSSVar('hello', {
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
delete globalThis.getComputedStyle;
|
||||||
|
|
||||||
|
t.ok(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
216
client/dom/io.js
216
client/dom/io.js
|
|
@ -1,216 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd*/
|
|
||||||
|
|
||||||
const itype = require('itype');
|
|
||||||
const {promisify} = require('es6-promisify');
|
|
||||||
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
|
|
||||||
const Images = require('./images');
|
|
||||||
const load = require('./load');
|
|
||||||
|
|
||||||
const imgPosition = {
|
|
||||||
top: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports._replaceHash = replaceHash;
|
|
||||||
function replaceHash(url) {
|
|
||||||
/*
|
|
||||||
* if we send ajax request -
|
|
||||||
* no need in hash so we escape #
|
|
||||||
*/
|
|
||||||
return url.replace(/#/g, '%23');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.delete = promisify((url, data, callback) => {
|
|
||||||
const isFunc = itype.function(data);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = data;
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method : 'DELETE',
|
|
||||||
url : FS + url,
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition : {
|
|
||||||
top: !!data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.patch = promisify((url, data, callback) => {
|
|
||||||
const isFunc = itype.function(data);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = data;
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.write = promisify((url, data, callback) => {
|
|
||||||
const isFunc = itype.function(data);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = data;
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.read = promisify((url, dataType, callback) => {
|
|
||||||
const notLog = !url.includes('?');
|
|
||||||
const isFunc = itype.function(dataType);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = dataType;
|
|
||||||
dataType = 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: FS + url,
|
|
||||||
callback,
|
|
||||||
notLog,
|
|
||||||
dataType,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.cp = promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/cp',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.pack = promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/pack',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.extract = promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/extract',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.mv = promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/mv',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports.Config = {
|
|
||||||
read: promisify((callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/config',
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
write: promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: '/config',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Markdown = {
|
|
||||||
read: promisify((url, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/markdown' + url,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
render: promisify((data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/markdown',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
function sendRequest(params) {
|
|
||||||
const p = params;
|
|
||||||
const {prefixURL} = CloudCmd;
|
|
||||||
|
|
||||||
p.url = prefixURL + p.url;
|
|
||||||
p.url = encodeURI(p.url);
|
|
||||||
|
|
||||||
p.url = replaceHash(p.url);
|
|
||||||
|
|
||||||
load.ajax({
|
|
||||||
method : p.method,
|
|
||||||
url : p.url,
|
|
||||||
data : p.data,
|
|
||||||
dataType : p.dataType,
|
|
||||||
error : (jqXHR) => {
|
|
||||||
const response = jqXHR.responseText;
|
|
||||||
|
|
||||||
const {
|
|
||||||
statusText,
|
|
||||||
status,
|
|
||||||
} = jqXHR;
|
|
||||||
|
|
||||||
const text = status === 404 ? response : statusText;
|
|
||||||
|
|
||||||
p.callback(Error(text));
|
|
||||||
},
|
|
||||||
success: (data) => {
|
|
||||||
Images.hide();
|
|
||||||
|
|
||||||
if (!p.notLog)
|
|
||||||
CloudCmd.log(data);
|
|
||||||
|
|
||||||
p.callback(null, data);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
166
client/dom/io/index.js
Normal file
166
client/dom/io/index.js
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
import {FS} from '#common/cloudfunc';
|
||||||
|
import {sendRequest as _sendRequest} from './send-request.js';
|
||||||
|
|
||||||
|
const {assign} = Object;
|
||||||
|
|
||||||
|
const imgPosition = {
|
||||||
|
top: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = async (url, data, overrides = {}) => {
|
||||||
|
const {
|
||||||
|
sendRequest = _sendRequest,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: FS + url,
|
||||||
|
imgPosition: {
|
||||||
|
top: Boolean(data),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
assign(request, {
|
||||||
|
data,
|
||||||
|
url: `${request.url}?files`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return await sendRequest(request);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const patch = async (url, data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: FS + url,
|
||||||
|
data,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const write = async (url, data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: FS + url,
|
||||||
|
data,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDirectory = async (url, overrides = {}) => {
|
||||||
|
const {
|
||||||
|
sendRequest = _sendRequest,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
|
return await sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: `${FS}${url}?dir`,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const read = async (url, dataType = 'text') => {
|
||||||
|
const notLog = !url.includes('?');
|
||||||
|
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: FS + url,
|
||||||
|
notLog,
|
||||||
|
dataType,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const copy = async (from, to, names) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/copy',
|
||||||
|
data: {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
names,
|
||||||
|
},
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pack = async (data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/pack',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extract = async (data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/extract',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const move = async (from, to, names) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/move',
|
||||||
|
data: {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
names,
|
||||||
|
},
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const rename = async (from, to) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/rename',
|
||||||
|
data: {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
},
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Config = {
|
||||||
|
read: async () => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/config',
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
write: async (data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: '/config',
|
||||||
|
data,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Markdown = {
|
||||||
|
read: async (url) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/markdown${url}`,
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: async (data) => {
|
||||||
|
return await _sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/markdown',
|
||||||
|
data,
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
61
client/dom/io/index.spec.js
Normal file
61
client/dom/io/index.spec.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import * as io from './index.js';
|
||||||
|
|
||||||
|
test('client: dom: io', (t) => {
|
||||||
|
const sendRequest = stub();
|
||||||
|
|
||||||
|
io.createDirectory('/hello', {
|
||||||
|
sendRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
imgPosition: {
|
||||||
|
top: true,
|
||||||
|
},
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/fs/hello?dir',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(sendRequest, [expected]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: dom: io: remove: no files', async (t) => {
|
||||||
|
const sendRequest = stub();
|
||||||
|
|
||||||
|
await io.remove('/hello', null, {
|
||||||
|
sendRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
imgPosition: {
|
||||||
|
top: false,
|
||||||
|
},
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/fs/hello',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(sendRequest, [expected]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: dom: io: remove: files', async (t) => {
|
||||||
|
const sendRequest = stub();
|
||||||
|
const files = ['world'];
|
||||||
|
|
||||||
|
await io.remove('/hello', files, {
|
||||||
|
sendRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
imgPosition: {
|
||||||
|
top: true,
|
||||||
|
},
|
||||||
|
data: ['world'],
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/fs/hello?files',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(sendRequest, [expected]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
48
client/dom/io/send-request.js
Normal file
48
client/dom/io/send-request.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* global CloudCmd */
|
||||||
|
import {promisify} from 'es6-promisify';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
import * as load from '#dom/load';
|
||||||
|
|
||||||
|
export const sendRequest = promisify((params, callback) => {
|
||||||
|
const p = params;
|
||||||
|
const {prefixURL} = CloudCmd;
|
||||||
|
|
||||||
|
p.url = prefixURL + p.url;
|
||||||
|
p.url = encodeURI(p.url);
|
||||||
|
|
||||||
|
p.url = replaceHash(p.url);
|
||||||
|
|
||||||
|
load.ajax({
|
||||||
|
method: p.method,
|
||||||
|
url: p.url,
|
||||||
|
data: p.data,
|
||||||
|
dataType: p.dataType,
|
||||||
|
error: (jqXHR) => {
|
||||||
|
const response = jqXHR.responseText;
|
||||||
|
|
||||||
|
const {statusText, status} = jqXHR;
|
||||||
|
|
||||||
|
const text = status === 404 ? response : statusText;
|
||||||
|
|
||||||
|
callback(Error(text));
|
||||||
|
},
|
||||||
|
success: (data) => {
|
||||||
|
Images.hide();
|
||||||
|
|
||||||
|
if (!p.notLog)
|
||||||
|
CloudCmd.log(data);
|
||||||
|
|
||||||
|
callback(null, data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const _replaceHash = replaceHash;
|
||||||
|
|
||||||
|
function replaceHash(url) {
|
||||||
|
/*
|
||||||
|
* if we send ajax request -
|
||||||
|
* no need in hash so we escape #
|
||||||
|
*/
|
||||||
|
return url.replace(/#/g, '%23');
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
'use strict';
|
import {test} from 'supertape';
|
||||||
|
import {_replaceHash} from './send-request.js';
|
||||||
const test = require('supertape');
|
|
||||||
const io = require('./io');
|
|
||||||
|
|
||||||
test('cloudcmd: client: io: replaceHash', (t) => {
|
test('cloudcmd: client: io: replaceHash', (t) => {
|
||||||
const {_replaceHash} = io;
|
|
||||||
const url = '/hello/####world';
|
const url = '/hello/####world';
|
||||||
const result = _replaceHash(url);
|
const result = _replaceHash(url);
|
||||||
const expected = '/hello/%23%23%23%23world';
|
const expected = '/hello/%23%23%23%23world';
|
||||||
|
|
||||||
t.equal(result, expected, 'should equal');
|
t.equal(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1,24 +1,21 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import {callbackify} from 'node:util';
|
||||||
|
import {rendy} from 'rendy';
|
||||||
|
import itype from 'itype';
|
||||||
|
import * as load from 'load.js';
|
||||||
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import {findObjByNameInArr} from '#common/util';
|
||||||
|
import * as Files from '#dom/files';
|
||||||
|
|
||||||
const rendy = require('rendy');
|
export const loadRemote = callbackify(async (name, options) => {
|
||||||
const itype = require('itype');
|
|
||||||
const load = require('load.js');
|
|
||||||
const tryToCatch = require('try-to-catch');
|
|
||||||
|
|
||||||
const {findObjByNameInArr} = require('../../common/util');
|
|
||||||
|
|
||||||
const Files = require('./files');
|
|
||||||
|
|
||||||
module.exports = (name, options, callback = options) => {
|
|
||||||
const {prefix, config} = CloudCmd;
|
const {prefix, config} = CloudCmd;
|
||||||
const o = options;
|
const o = options;
|
||||||
|
|
||||||
if (o.name && window[o.name])
|
if (o.name && window[o.name])
|
||||||
return callback();
|
return;
|
||||||
|
|
||||||
|
const modules = await Files.get('modules');
|
||||||
|
|
||||||
Files.get('modules').then(async (modules) => {
|
|
||||||
const online = config('online') && navigator.onLine;
|
const online = config('online') && navigator.onLine;
|
||||||
const module = findObjByNameInArr(modules.remote, name);
|
const module = findObjByNameInArr(modules.remote, name);
|
||||||
|
|
||||||
|
|
@ -30,31 +27,32 @@ module.exports = (name, options, callback = options) => {
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
remoteTmpls = module.remote;
|
remoteTmpls = module.remote;
|
||||||
local = module.local;
|
({local} = module);
|
||||||
} else {
|
} else {
|
||||||
remoteTmpls = [module.remote];
|
remoteTmpls = [module.remote];
|
||||||
local = [module.local];
|
local = [module.local];
|
||||||
}
|
}
|
||||||
|
|
||||||
const localURL = local.map((url) => {
|
const localURL = [];
|
||||||
return prefix + url;
|
|
||||||
});
|
|
||||||
|
|
||||||
const remoteURL = remoteTmpls.map((tmpl) => {
|
for (const url of local) {
|
||||||
return rendy(tmpl, {
|
localURL.push(prefix + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteURL = [];
|
||||||
|
|
||||||
|
for (const tmpl of remoteTmpls) {
|
||||||
|
remoteURL.push(rendy(tmpl, {
|
||||||
version,
|
version,
|
||||||
});
|
}));
|
||||||
});
|
}
|
||||||
|
|
||||||
if (online) {
|
if (online) {
|
||||||
const [e] = await tryToCatch(load.parallel, remoteURL);
|
const [e] = await tryToCatch(load.parallel, remoteURL);
|
||||||
|
|
||||||
if (!e)
|
if (!e)
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [e] = await tryToCatch(load.parallel, localURL);
|
await load.parallel(localURL);
|
||||||
callback(e);
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
'use strict';
|
import itype from 'itype';
|
||||||
|
import jonny from 'jonny';
|
||||||
const itype = require('itype');
|
import Emitify from 'emitify';
|
||||||
const jonny = require('jonny');
|
import exec from 'execon';
|
||||||
const Emitify = require('emitify');
|
import * as Images from '#dom/images';
|
||||||
const exec = require('execon');
|
|
||||||
const Images = require('./images');
|
|
||||||
|
|
||||||
module.exports.getIdBySrc = getIdBySrc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function gets id by src
|
* Function gets id by src
|
||||||
|
|
@ -14,17 +10,18 @@ module.exports.getIdBySrc = getIdBySrc;
|
||||||
*
|
*
|
||||||
* Example: http://domain.com/1.js -> 1_js
|
* Example: http://domain.com/1.js -> 1_js
|
||||||
*/
|
*/
|
||||||
function getIdBySrc(src) {
|
export function getIdBySrc(src) {
|
||||||
const isStr = itype.string(src);
|
const isStr = itype.string(src);
|
||||||
|
|
||||||
if (!isStr)
|
if (!isStr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (~src.indexOf(':'))
|
if (src.includes(':'))
|
||||||
src += '-join';
|
src += '-join';
|
||||||
|
|
||||||
const num = src.lastIndexOf('/') + 1;
|
const num = src.lastIndexOf('/') + 1;
|
||||||
const sub = src.substr(src, num);
|
const sub = src.substr(src, num);
|
||||||
|
|
||||||
const id = src
|
const id = src
|
||||||
.replace(sub, '')
|
.replace(sub, '')
|
||||||
.replace(/\./g, '-');
|
.replace(/\./g, '-');
|
||||||
|
|
@ -37,15 +34,15 @@ function getIdBySrc(src) {
|
||||||
*
|
*
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
module.exports.ajax = (params) => {
|
export const ajax = (params) => {
|
||||||
const p = params;
|
const p = params;
|
||||||
const isObject = itype.object(p.data);
|
const isObject = itype.object(p.data);
|
||||||
const isArray = itype.array(p.data);
|
const isArray = itype.array(p.data);
|
||||||
const isArrayBuf = itype(p.data) === 'arraybuffer';
|
const isArrayBuf = itype(p.data) === 'arraybuffer';
|
||||||
const type = p.type || p.method || 'GET';
|
const type = p.type || p.method || 'GET';
|
||||||
const {
|
|
||||||
headers = {},
|
const {headers = {}} = p;
|
||||||
} = p;
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.open(type, p.url, true);
|
xhr.open(type, p.url, true);
|
||||||
|
|
@ -63,7 +60,7 @@ module.exports.ajax = (params) => {
|
||||||
if (!isArrayBuf && isObject || isArray)
|
if (!isArrayBuf && isObject || isArray)
|
||||||
data = jonny.stringify(p.data);
|
data = jonny.stringify(p.data);
|
||||||
else
|
else
|
||||||
data = p.data;
|
({data} = p);
|
||||||
|
|
||||||
xhr.onreadystatechange = (event) => {
|
xhr.onreadystatechange = (event) => {
|
||||||
const xhr = event.target;
|
const xhr = event.target;
|
||||||
|
|
@ -81,7 +78,7 @@ module.exports.ajax = (params) => {
|
||||||
return exec(p.error, xhr);
|
return exec(p.error, xhr);
|
||||||
|
|
||||||
const notText = p.dataType !== 'text';
|
const notText = p.dataType !== 'text';
|
||||||
const isContain = ~type.indexOf(TYPE_JSON);
|
const isContain = type.includes(TYPE_JSON);
|
||||||
|
|
||||||
let data = xhr.response;
|
let data = xhr.response;
|
||||||
|
|
||||||
|
|
@ -94,12 +91,11 @@ module.exports.ajax = (params) => {
|
||||||
xhr.send(data);
|
xhr.send(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.put = (url, body) => {
|
export const put = (url, body) => {
|
||||||
const emitter = Emitify();
|
const emitter = Emitify();
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
url = encodeURI(url)
|
url = encodeURI(url).replace(/#/g, '#');
|
||||||
.replace('#', '%23');
|
|
||||||
|
|
||||||
xhr.open('put', url, true);
|
xhr.open('put', url, true);
|
||||||
|
|
||||||
|
|
@ -120,8 +116,10 @@ module.exports.put = (url, body) => {
|
||||||
if (!over)
|
if (!over)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xhr.status === OK)
|
if (xhr.status === OK) {
|
||||||
|
emitter.emit('progress', 100);
|
||||||
return emitter.emit('end');
|
return emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
const error = Error(xhr.responseText);
|
const error = Error(xhr.responseText);
|
||||||
emitter.emit('error', error);
|
emitter.emit('error', error);
|
||||||
|
|
@ -131,4 +129,3 @@ module.exports.put = (url, body) => {
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import capitalize from 'just-capitalize';
|
||||||
|
import * as _Dialog from '#dom/dialog';
|
||||||
|
import * as Storage from '#dom/storage';
|
||||||
|
import * as RESTful from '#dom/rest';
|
||||||
|
import * as _currentFile from '../current-file.js';
|
||||||
|
|
||||||
const capitalize = require('just-capitalize');
|
export default async (current, overrides = {}) => {
|
||||||
|
const {
|
||||||
|
refresh = CloudCmd.refresh,
|
||||||
|
Dialog = _Dialog,
|
||||||
|
currentFile = _currentFile,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
const Dialog = require('../dialog');
|
|
||||||
const Storage = require('../storage');
|
|
||||||
const RESTful = require('../rest');
|
|
||||||
const {
|
const {
|
||||||
isCurrentFile,
|
isCurrentFile,
|
||||||
getCurrentName,
|
getCurrentName,
|
||||||
|
|
@ -15,9 +20,8 @@ const {
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
setCurrentName,
|
setCurrentName,
|
||||||
} = require('../current-file');
|
} = currentFile;
|
||||||
|
|
||||||
module.exports = async (current) => {
|
|
||||||
if (!isCurrentFile(current))
|
if (!isCurrentFile(current))
|
||||||
current = getCurrentFile();
|
current = getCurrentFile();
|
||||||
|
|
||||||
|
|
@ -46,12 +50,11 @@ module.exports = async (current) => {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const dirPath = getCurrentDirPath();
|
const dirPath = getCurrentDirPath();
|
||||||
const files = {
|
|
||||||
from : dirPath + from,
|
|
||||||
to : dirPath + to,
|
|
||||||
};
|
|
||||||
|
|
||||||
const [e] = await RESTful.mv(files);
|
const fromFull = `${dirPath}${from}`;
|
||||||
|
const toFull = `${dirPath}${to}`;
|
||||||
|
|
||||||
|
const [e] = await RESTful.rename(fromFull, toFull);
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
return;
|
return;
|
||||||
|
|
@ -59,5 +62,5 @@ module.exports = async (current) => {
|
||||||
setCurrentName(to, current);
|
setCurrentName(to, current);
|
||||||
|
|
||||||
Storage.remove(dirPath);
|
Storage.remove(dirPath);
|
||||||
CloudCmd.refresh();
|
refresh();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,26 @@
|
||||||
'use strict';
|
import {test, stub} from 'supertape';
|
||||||
|
import renameCurrent from './rename-current.js';
|
||||||
const test = require('supertape');
|
|
||||||
const stub = require('@cloudcmd/stub');
|
|
||||||
const mockRequire = require('mock-require');
|
|
||||||
|
|
||||||
const {reRequire} = mockRequire;
|
|
||||||
|
|
||||||
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
|
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
|
||||||
const current = {};
|
const current = {};
|
||||||
const isCurrentFile = stub();
|
const isCurrentFile = stub();
|
||||||
|
|
||||||
mockRequire('../dialog', stubDialog());
|
const currentFile = stubCurrentFile({
|
||||||
mockRequire('../current-file', stubCurrentFile({
|
|
||||||
isCurrentFile,
|
isCurrentFile,
|
||||||
}));
|
});
|
||||||
|
|
||||||
const renameCurrent = reRequire('./rename-current');
|
await renameCurrent(current, {
|
||||||
await renameCurrent(current);
|
Dialog: stubDialog(),
|
||||||
|
currentFile,
|
||||||
|
});
|
||||||
|
|
||||||
t.ok(isCurrentFile.calledWith(current), 'should call isCurrentFile');
|
t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
|
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
|
||||||
const current = {};
|
const current = {};
|
||||||
const name = 'hello';
|
const name = 'hello';
|
||||||
const {CloudCmd} = global;
|
|
||||||
|
|
||||||
const CloudCmdStub = {
|
|
||||||
refresh: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
global.CloudCmd = CloudCmdStub;
|
|
||||||
|
|
||||||
const prompt = stub().returns([null, name]);
|
const prompt = stub().returns([null, name]);
|
||||||
const confirm = stub().returns([true]);
|
const confirm = stub().returns([true]);
|
||||||
|
|
@ -39,23 +28,24 @@ test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
|
||||||
const getCurrentByName = stub().returns(current);
|
const getCurrentByName = stub().returns(current);
|
||||||
const getCurrentType = stub().returns('directory');
|
const getCurrentType = stub().returns('directory');
|
||||||
|
|
||||||
mockRequire('../dialog', stubDialog({
|
const Dialog = stubDialog({
|
||||||
confirm,
|
confirm,
|
||||||
prompt,
|
prompt,
|
||||||
}));
|
});
|
||||||
|
|
||||||
mockRequire('../current-file', stubCurrentFile({
|
const currentFile = stubCurrentFile({
|
||||||
getCurrentByName,
|
getCurrentByName,
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
}));
|
});
|
||||||
|
|
||||||
const renameCurrent = reRequire('./rename-current');
|
await renameCurrent(null, {
|
||||||
await renameCurrent();
|
Dialog,
|
||||||
|
currentFile,
|
||||||
|
});
|
||||||
|
|
||||||
const expected = 'Directory "hello" already exists. Proceed?';
|
const expected = 'Directory "hello" already exists. Proceed?';
|
||||||
global.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.ok(confirm.calledWith(expected), 'should call confirm');
|
t.calledWith(confirm, [expected], 'should call confirm');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -94,4 +84,3 @@ const stubCurrentFile = (fns = {}) => {
|
||||||
setCurrentName,
|
setCurrentName,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
'use strict';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import * as Dialog from '#dom/dialog';
|
||||||
const tryToCatch = require('try-to-catch');
|
import * as Images from '#dom/images';
|
||||||
|
import {encode} from '#common/entity';
|
||||||
const {encode} = require('../../common/entity');
|
import * as IO from './io/index.js';
|
||||||
|
|
||||||
const Images = require('./images');
|
|
||||||
const IO = require('./io');
|
|
||||||
const Dialog = require('./dialog');
|
|
||||||
|
|
||||||
const handleError = (promise) => async (...args) => {
|
const handleError = (promise) => async (...args) => {
|
||||||
const [e, data] = await tryToCatch(promise, ...args);
|
const [e, data] = await tryToCatch(promise, ...args);
|
||||||
|
|
@ -22,22 +18,23 @@ const handleError = (promise) => async (...args) => {
|
||||||
return [e, data];
|
return [e, data];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.delete = handleError(IO.delete);
|
export const remove = handleError(IO.remove);
|
||||||
module.exports.patch = handleError(IO.patch);
|
export const patch = handleError(IO.patch);
|
||||||
module.exports.write = handleError(IO.write);
|
export const write = handleError(IO.write);
|
||||||
module.exports.read = handleError(IO.read);
|
export const createDirectory = handleError(IO.createDirectory);
|
||||||
module.exports.cp = handleError(IO.cp);
|
export const read = handleError(IO.read);
|
||||||
module.exports.pack = handleError(IO.pack);
|
export const copy = handleError(IO.copy);
|
||||||
module.exports.extract = handleError(IO.extract);
|
export const pack = handleError(IO.pack);
|
||||||
module.exports.mv = handleError(IO.mv);
|
export const extract = handleError(IO.extract);
|
||||||
|
export const move = handleError(IO.move);
|
||||||
|
export const rename = handleError(IO.rename);
|
||||||
|
|
||||||
module.exports.Config = {
|
export const Config = {
|
||||||
read: handleError(IO.Config.read),
|
read: handleError(IO.Config.read),
|
||||||
write: handleError(IO.Config.write),
|
write: handleError(IO.Config.write),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.Markdown = {
|
export const Markdown = {
|
||||||
read: handleError(IO.Markdown.read),
|
read: handleError(IO.Markdown.read),
|
||||||
render: handleError(IO.Markdown.render),
|
render: handleError(IO.Markdown.render),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,18 @@
|
||||||
'use strict';
|
import {alert, prompt} from '#dom/dialog';
|
||||||
|
import {getRegExp} from '#common/util';
|
||||||
|
import {getCurrentName} from './current-file.js';
|
||||||
|
import {
|
||||||
|
isSelected,
|
||||||
|
toggleSelectedFile,
|
||||||
|
} from './cmd.js';
|
||||||
|
|
||||||
let SelectType = '*.*';
|
let SelectType = '*.*';
|
||||||
|
|
||||||
const {getRegExp} = require('../../common/util');
|
export const selectByPattern = async (msg, files) => {
|
||||||
const {
|
|
||||||
alert,
|
|
||||||
prompt,
|
|
||||||
} = require('./dialog');
|
|
||||||
|
|
||||||
const DOM = require('.');
|
|
||||||
|
|
||||||
module.exports = async (msg, files) => {
|
|
||||||
if (!files)
|
if (!files)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const allMsg = `Specify file type for ${msg} selection`;
|
const allMsg = `Specify file type for ${msg} selection`;
|
||||||
|
|
||||||
/* eslint require-atomic-updates: 0 */
|
|
||||||
const [cancel, type] = await prompt(allMsg, SelectType);
|
const [cancel, type] = await prompt(allMsg, SelectType);
|
||||||
|
|
||||||
if (cancel)
|
if (cancel)
|
||||||
|
|
@ -28,24 +24,23 @@ module.exports = async (msg, files) => {
|
||||||
let matches = 0;
|
let matches = 0;
|
||||||
|
|
||||||
for (const current of files) {
|
for (const current of files) {
|
||||||
const name = DOM.getCurrentName(current);
|
const name = getCurrentName(current);
|
||||||
|
|
||||||
if (name === '..' || !regExp.test(name))
|
if (name === '..' || !regExp.test(name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
++matches;
|
++matches;
|
||||||
|
|
||||||
let isSelected = DOM.isSelected(current);
|
let selected = isSelected(current);
|
||||||
const shouldSel = msg === 'expand';
|
const shouldSel = msg === 'expand';
|
||||||
|
|
||||||
if (shouldSel)
|
if (shouldSel)
|
||||||
isSelected = !isSelected;
|
selected = !selected;
|
||||||
|
|
||||||
if (isSelected)
|
if (selected)
|
||||||
DOM.toggleSelectedFile(current);
|
toggleSelectedFile(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matches)
|
if (!matches)
|
||||||
alert('No matches found!');
|
alert('No matches found!');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,26 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const tryCatch = require('try-catch');
|
|
||||||
|
|
||||||
const {parse, stringify} = JSON;
|
const {parse, stringify} = JSON;
|
||||||
const isObj = (a) => typeof a === 'object';
|
|
||||||
|
|
||||||
module.exports.set = (name, data) => {
|
export const set = (name, data) => {
|
||||||
const primitive = !isObj(data) ? data : stringify(data);
|
localStorage.setItem(name, data);
|
||||||
|
|
||||||
localStorage.setItem(name, primitive);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.get = async (name) => {
|
export const setJson = (name, data) => {
|
||||||
|
localStorage.setItem(name, stringify(data));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get = (name) => {
|
||||||
|
return localStorage.getItem(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getJson = (name) => {
|
||||||
const data = localStorage.getItem(name);
|
const data = localStorage.getItem(name);
|
||||||
const [, result = data] = tryCatch(parse, data);
|
return parse(data);
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.clear = () => {
|
export const clear = () => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.remove = (item) => {
|
export const remove = (item) => {
|
||||||
localStorage.removeItem(item);
|
localStorage.removeItem(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.removeMatch = (string) => {
|
|
||||||
const reg = RegExp('^' + string + '.*$');
|
|
||||||
const test = (a) => reg.test(a);
|
|
||||||
const remove = (a) => localStorage.removeItem(a);
|
|
||||||
|
|
||||||
Object.keys(localStorage)
|
|
||||||
.filter(test)
|
|
||||||
.forEach(remove);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
105
client/dom/storage.spec.js
Normal file
105
client/dom/storage.spec.js
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import * as storage from '#dom/storage';
|
||||||
|
|
||||||
|
const {stringify} = JSON;
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: set', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const setItem = stub();
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
setItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
await storage.set('hello', 'world');
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.calledWith(setItem, ['hello', 'world'], 'should call setItem');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: get', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const getItem = stub().returns('world');
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
getItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await storage.get('hello');
|
||||||
|
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.equal(result, 'world');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: getJson', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const expected = {
|
||||||
|
hello: 'world',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItem = stub().returns(stringify(expected));
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
getItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await storage.getJson('hello');
|
||||||
|
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: setJson', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const data = {
|
||||||
|
hello: 'world',
|
||||||
|
};
|
||||||
|
|
||||||
|
const expected = stringify(data);
|
||||||
|
const setItem = stub();
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
setItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
await storage.setJson('hello', data);
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.calledWith(setItem, ['hello', expected]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: remove', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const removeItem = stub();
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
removeItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
await storage.remove('hello');
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.calledWith(removeItem, ['hello'], 'should call removeItem');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: storage: clear', async (t) => {
|
||||||
|
const {localStorage} = globalThis;
|
||||||
|
const clear = stub();
|
||||||
|
|
||||||
|
globalThis.localStorage = {
|
||||||
|
clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
await storage.clear();
|
||||||
|
globalThis.localStorage = localStorage;
|
||||||
|
|
||||||
|
t.calledWithNoArgs(clear, 'should call clear');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,25 +1,19 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import {eachSeries} from 'execon';
|
||||||
|
import wraptile from 'wraptile';
|
||||||
|
import * as load from '#dom/load';
|
||||||
|
import {alert} from '#dom/dialog';
|
||||||
|
import {FS} from '#common/cloudfunc';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
import {getCurrentDirPath} from './current-file.js';
|
||||||
|
|
||||||
const {eachSeries} = require('execon');
|
|
||||||
const wraptile = require('wraptile');
|
|
||||||
|
|
||||||
const load = require('./load');
|
|
||||||
const Images = require('./images');
|
|
||||||
const {alert} = require('./dialog');
|
|
||||||
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
|
|
||||||
const onEnd = wraptile(_onEnd);
|
|
||||||
const loadFile = wraptile(_loadFile);
|
const loadFile = wraptile(_loadFile);
|
||||||
|
const onEnd = wraptile(_onEnd);
|
||||||
|
|
||||||
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.');
|
export const uploadFiles = (dir, files) => {
|
||||||
|
|
||||||
module.exports = (dir, files) => {
|
|
||||||
if (!files) {
|
if (!files) {
|
||||||
files = dir;
|
files = dir;
|
||||||
dir = getPathWhenRootEmpty();
|
dir = getCurrentDirPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = files.length;
|
const n = files.length;
|
||||||
|
|
@ -55,7 +49,8 @@ function _loadFile(dir, n, file, callback) {
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
load.put(api + path, file)
|
load
|
||||||
|
.put(api + path, file)
|
||||||
.on('error', showError)
|
.on('error', showError)
|
||||||
.on('end', callback)
|
.on('end', callback)
|
||||||
.on('progress', (count) => {
|
.on('progress', (count) => {
|
||||||
|
|
@ -70,4 +65,3 @@ function _loadFile(dir, n, file, callback) {
|
||||||
function showError({message}) {
|
function showError({message}) {
|
||||||
alert(message);
|
alert(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция генерирует JSON из html-таблицы файлов и
|
* Функция генерирует JSON из html-таблицы файлов и
|
||||||
* используеться при первом заходе в корень
|
* используеться при первом заходе в корень
|
||||||
*/
|
*/
|
||||||
module.exports = () => {
|
export const getJsonFromFileTable = () => {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const path = DOM.getCurrentDirPath();
|
const path = DOM.getCurrentDirPath();
|
||||||
const infoFiles = Info.files || [];
|
const infoFiles = Info.files || [];
|
||||||
|
|
||||||
|
const files = infoFiles
|
||||||
|
.filter(notParent)
|
||||||
|
.map(parse);
|
||||||
|
|
||||||
|
const fileTable = {
|
||||||
|
path,
|
||||||
|
files,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fileTable;
|
||||||
|
};
|
||||||
|
|
||||||
const notParent = (current) => {
|
const notParent = (current) => {
|
||||||
const name = DOM.getCurrentName(current);
|
const name = DOM.getCurrentName(current);
|
||||||
return name !== '..';
|
return name !== '..';
|
||||||
|
|
@ -34,16 +42,3 @@ module.exports = () => {
|
||||||
type,
|
type,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const files = infoFiles
|
|
||||||
.filter(notParent)
|
|
||||||
.map(parse);
|
|
||||||
|
|
||||||
const fileTable = {
|
|
||||||
path,
|
|
||||||
files,
|
|
||||||
};
|
|
||||||
|
|
||||||
return fileTable;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
15
client/key/binder.js
Normal file
15
client/key/binder.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
export const createBinder = () => {
|
||||||
|
let binded = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isBind() {
|
||||||
|
return binded;
|
||||||
|
},
|
||||||
|
setBind() {
|
||||||
|
binded = true;
|
||||||
|
},
|
||||||
|
unsetBind() {
|
||||||
|
binded = false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
28
client/key/binder.spec.js
Normal file
28
client/key/binder.spec.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {test} from 'supertape';
|
||||||
|
import {createBinder} from './binder.js';
|
||||||
|
|
||||||
|
test('client: key: binder: isBind: default', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
t.notOk(binder.isBind(), 'should not be bind by default');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: key: binder: setBind', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
binder.setBind();
|
||||||
|
|
||||||
|
t.ok(binder.isBind(), 'should be bind');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: key: binder: unsetBind', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
binder.setBind();
|
||||||
|
binder.unsetBind();
|
||||||
|
|
||||||
|
t.notOk(binder.isBind(), 'should not be bind');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,91 +1,92 @@
|
||||||
/* global CloudCmd, DOM */
|
/* global CloudCmd, DOM */
|
||||||
|
import clipboard from '@cloudcmd/clipboard';
|
||||||
|
import {fullstore} from 'fullstore';
|
||||||
|
import * as Events from '#dom/events';
|
||||||
|
import * as Buffer from '../dom/buffer.js';
|
||||||
|
import * as KEY from './key.js';
|
||||||
|
import _vim from './vim/index.js';
|
||||||
|
import setCurrentByChar from './set-current-by-char.js';
|
||||||
|
import {createBinder} from './binder.js';
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
|
|
||||||
const exec = require('execon');
|
|
||||||
const clipboard = require('@cloudcmd/clipboard');
|
|
||||||
|
|
||||||
const Events = require('../dom/events');
|
|
||||||
const Buffer = require('../dom/buffer');
|
|
||||||
const KEY = require('./key');
|
|
||||||
const vim = require('./vim');
|
|
||||||
const setCurrentByChar = require('./set-current-by-char');
|
|
||||||
const fullstore = require('fullstore');
|
|
||||||
const Chars = fullstore();
|
const Chars = fullstore();
|
||||||
|
|
||||||
|
const toggleVim = (keyCode, overrides = {}) => {
|
||||||
|
const {_config, config} = overrides;
|
||||||
|
|
||||||
|
if (!config('vim') && keyCode === KEY.ESC)
|
||||||
|
_config('vim', true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isUndefined = (a) => typeof a === 'undefined';
|
||||||
|
|
||||||
Chars([]);
|
Chars([]);
|
||||||
|
|
||||||
KeyProto.prototype = KEY;
|
const {assign} = Object;
|
||||||
CloudCmd.Key = KeyProto;
|
const binder = createBinder();
|
||||||
const {loadDir} = CloudCmd;
|
|
||||||
|
|
||||||
function KeyProto() {
|
const bind = () => {
|
||||||
let Binded;
|
|
||||||
|
|
||||||
const Key = this;
|
|
||||||
|
|
||||||
this.isBind = () => {
|
|
||||||
return Binded;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setBind = () => {
|
|
||||||
Binded = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unsetBind = () => {
|
|
||||||
Binded = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.bind = () => {
|
|
||||||
Events.addKey(listener, true);
|
Events.addKey(listener, true);
|
||||||
Binded = true;
|
binder.setBind();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Key = assign(binder, KEY, {
|
||||||
|
bind,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const _listener = listener;
|
||||||
|
|
||||||
function getChar(event) {
|
function getChar(event) {
|
||||||
/*
|
/*
|
||||||
* event.keyIdentifier deprecated in chrome v51
|
* event.keyIdentifier deprecated in chrome v51
|
||||||
* but event.key is absent in chrome <= v51
|
* but event.key is absent in chrome <= v51
|
||||||
*/
|
*/
|
||||||
|
const {
|
||||||
|
key,
|
||||||
|
shift,
|
||||||
|
keyCode,
|
||||||
|
keyIdentifier,
|
||||||
|
} = event;
|
||||||
|
|
||||||
if (event.key)
|
const char = key || fromCharCode(keyIdentifier);
|
||||||
return event.key;
|
const symbol = getSymbol(shift, keyCode);
|
||||||
|
|
||||||
return fromCharCode(event.keyIdentifier);
|
return [symbol, char];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listener(event) {
|
async function listener(event, overrides = {}) {
|
||||||
|
const {
|
||||||
|
config = CloudCmd.config,
|
||||||
|
_config = CloudCmd._config,
|
||||||
|
switchKey = _switchKey,
|
||||||
|
vim = _vim,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
const {keyCode} = event;
|
const {keyCode} = event;
|
||||||
|
|
||||||
// strange chrome bug calles listener twice
|
// strange chrome bug calls listener twice
|
||||||
// in second time event misses a lot fields
|
// in second time event misses a lot fields
|
||||||
if (typeof event.altKey === 'undefined')
|
if (isUndefined(event.altKey))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const alt = event.altKey;
|
const alt = event.altKey;
|
||||||
const ctrl = event.ctrlKey;
|
const ctrl = event.ctrlKey;
|
||||||
const shift = event.shiftKey;
|
|
||||||
const meta = event.metaKey;
|
const meta = event.metaKey;
|
||||||
const isBetween = keyCode >= KEY.ZERO && keyCode <= KEY.Z;
|
const isBetween = keyCode >= KEY.ZERO && keyCode <= KEY.Z;
|
||||||
const isNumpad = /Numpad/.test(event.code);
|
const isNumpad = /Numpad/.test(event.code);
|
||||||
|
|
||||||
let char = getChar(event);
|
const [symbol, char] = getChar(event);
|
||||||
let isSymbol = ~['.', '_', '-', '+', '='].indexOf(char);
|
|
||||||
|
|
||||||
if (!isSymbol) {
|
if (!binder.isBind())
|
||||||
isSymbol = getSymbol(shift, keyCode);
|
|
||||||
|
|
||||||
if (isSymbol)
|
|
||||||
char = isSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Key.isBind())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const isVim = CloudCmd.config('vim');
|
toggleVim(keyCode, {
|
||||||
|
config,
|
||||||
|
_config,
|
||||||
|
});
|
||||||
|
|
||||||
if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || isSymbol))
|
const isVim = config('vim');
|
||||||
|
|
||||||
|
if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol))
|
||||||
return setCurrentByChar(char, Chars);
|
return setCurrentByChar(char, Chars);
|
||||||
|
|
||||||
Chars([]);
|
Chars([]);
|
||||||
|
|
@ -109,17 +110,19 @@ function KeyProto() {
|
||||||
case KEY.EQUAL:
|
case KEY.EQUAL:
|
||||||
return shift ? '+' : '=';
|
return shift ? '+' : '=';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromCharCode(keyIdentifier) {
|
function fromCharCode(keyIdentifier) {
|
||||||
const code = keyIdentifier.substring(2);
|
const code = keyIdentifier.substring(2);
|
||||||
const hex = parseInt(code, 16);
|
const hex = parseInt(code, 16);
|
||||||
const char = String.fromCharCode(hex);
|
|
||||||
|
|
||||||
return char;
|
return String.fromCharCode(hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchKey(event) {
|
async function _switchKey(event) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
let i;
|
let i;
|
||||||
let isSelected;
|
let isSelected;
|
||||||
let prev;
|
let prev;
|
||||||
|
|
@ -134,7 +137,12 @@ function KeyProto() {
|
||||||
isDir,
|
isDir,
|
||||||
} = Info;
|
} = Info;
|
||||||
|
|
||||||
const {Operation} = CloudCmd;
|
const {
|
||||||
|
Operation,
|
||||||
|
changeDir,
|
||||||
|
config,
|
||||||
|
} = CloudCmd;
|
||||||
|
|
||||||
const {keyCode} = event;
|
const {keyCode} = event;
|
||||||
|
|
||||||
const alt = event.altKey;
|
const alt = event.altKey;
|
||||||
|
|
@ -149,68 +157,72 @@ function KeyProto() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(keyCode) {
|
switch(keyCode) {
|
||||||
case Key.TAB:
|
case KEY.TAB:
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.INSERT:
|
case KEY.INSERT:
|
||||||
DOM .toggleSelectedFile(current)
|
DOM
|
||||||
|
.toggleSelectedFile(current)
|
||||||
.setCurrentFile(next);
|
.setCurrentFile(next);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.INSERT_MAC:
|
case KEY.INSERT_MAC:
|
||||||
DOM .toggleSelectedFile(current)
|
DOM
|
||||||
|
.toggleSelectedFile(current)
|
||||||
.setCurrentFile(next);
|
.setCurrentFile(next);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.DELETE:
|
case KEY.DELETE:
|
||||||
if (shift)
|
if (shift)
|
||||||
Operation.show('delete:silent');
|
Operation.show('delete:silent');
|
||||||
else
|
else
|
||||||
Operation.show('delete');
|
Operation.show('delete');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.ASTERISK:
|
case KEY.ASTERISK:
|
||||||
DOM.toggleAllSelectedFiles(current);
|
DOM.toggleAllSelectedFiles(current);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.PLUS:
|
case KEY.PLUS:
|
||||||
DOM.expandSelection();
|
DOM.expandSelection();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.MINUS:
|
case KEY.MINUS:
|
||||||
DOM.shrinkSelection();
|
DOM.shrinkSelection();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F1:
|
case KEY.F1:
|
||||||
CloudCmd.Help.show();
|
CloudCmd.Help.show();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F2:
|
case KEY.F2:
|
||||||
CloudCmd.UserMenu.show();
|
CloudCmd.UserMenu.show();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F3:
|
case KEY.F3:
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
if (Info.isDir)
|
if (Info.isDir)
|
||||||
await loadDir({
|
await changeDir(path);
|
||||||
path,
|
|
||||||
});
|
|
||||||
else if (shift)
|
else if (shift)
|
||||||
CloudCmd.Markdown.show(path);
|
CloudCmd.View.show(null, {
|
||||||
|
raw: true,
|
||||||
|
});
|
||||||
else if (ctrlMeta)
|
else if (ctrlMeta)
|
||||||
CloudCmd.sortPanel('name');
|
CloudCmd.sortPanel('name');
|
||||||
else
|
else
|
||||||
CloudCmd.View.show();
|
CloudCmd.View.show();
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F4:
|
case KEY.F4:
|
||||||
if (shift)
|
if (config('vim'))
|
||||||
CloudCmd.EditFileVim.show();
|
CloudCmd.EditFileVim.show();
|
||||||
else
|
else
|
||||||
CloudCmd.EditFile.show();
|
CloudCmd.EditFile.show();
|
||||||
|
|
@ -218,7 +230,7 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F5:
|
case KEY.F5:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
CloudCmd.sortPanel('date');
|
CloudCmd.sortPanel('date');
|
||||||
else if (alt)
|
else if (alt)
|
||||||
|
|
@ -229,7 +241,7 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F6:
|
case KEY.F6:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
CloudCmd.sortPanel('size');
|
CloudCmd.sortPanel('size');
|
||||||
else if (shift)
|
else if (shift)
|
||||||
|
|
@ -240,7 +252,7 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F7:
|
case KEY.F7:
|
||||||
if (shift)
|
if (shift)
|
||||||
DOM.promptNewFile();
|
DOM.promptNewFile();
|
||||||
else
|
else
|
||||||
|
|
@ -249,25 +261,26 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F8:
|
case KEY.F8:
|
||||||
Operation.show('delete');
|
Operation.show('delete');
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F9:
|
case KEY.F9:
|
||||||
if (alt)
|
if (alt)
|
||||||
Operation.show('extract');
|
Operation.show('extract');
|
||||||
else
|
else
|
||||||
CloudCmd.Menu.show();
|
CloudCmd.Menu.show();
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.F10:
|
case KEY.F10:
|
||||||
CloudCmd.Config.show();
|
CloudCmd.Config.show();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.TRA:
|
case KEY.TRA:
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (shift)
|
if (shift)
|
||||||
|
|
@ -281,32 +294,33 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.SPACE:
|
case KEY.SPACE:
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
if (!isDir || name === '..')
|
if (!isDir || name === '..')
|
||||||
isSelected = true;
|
isSelected = true;
|
||||||
else
|
else
|
||||||
isSelected = DOM.isSelected(current);
|
isSelected = DOM.isSelected(current);
|
||||||
|
|
||||||
exec.if(isSelected, () => {
|
if (!isSelected)
|
||||||
DOM.toggleSelectedFile(current);
|
await DOM.loadCurrentSize(current);
|
||||||
}, (callback) => {
|
|
||||||
DOM.loadCurrentSize(current, callback);
|
DOM.toggleSelectedFile(current);
|
||||||
});
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.U:
|
case KEY.U:
|
||||||
if (ctrlMeta) {
|
if (ctrlMeta) {
|
||||||
DOM.swapPanels();
|
DOM.swapPanels();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* navigation on file table: *
|
/* navigation on file table: *
|
||||||
* in case of pressing button 'up', *
|
* in case of pressing button 'up', *
|
||||||
* select previous row */
|
* select previous row */
|
||||||
case Key.UP:
|
case KEY.UP:
|
||||||
if (shift)
|
if (shift)
|
||||||
DOM.toggleSelectedFile(current);
|
DOM.toggleSelectedFile(current);
|
||||||
|
|
||||||
|
|
@ -316,7 +330,7 @@ function KeyProto() {
|
||||||
|
|
||||||
/* in case of pressing button 'down', *
|
/* in case of pressing button 'down', *
|
||||||
* select next row */
|
* select next row */
|
||||||
case Key.DOWN:
|
case KEY.DOWN:
|
||||||
if (shift)
|
if (shift)
|
||||||
DOM.toggleSelectedFile(current);
|
DOM.toggleSelectedFile(current);
|
||||||
|
|
||||||
|
|
@ -324,7 +338,7 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.LEFT:
|
case KEY.LEFT:
|
||||||
if (!alt)
|
if (!alt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -337,7 +351,7 @@ function KeyProto() {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.RIGHT:
|
case KEY.RIGHT:
|
||||||
if (!alt)
|
if (!alt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -352,19 +366,19 @@ function KeyProto() {
|
||||||
|
|
||||||
/* in case of pressing button 'Home', *
|
/* in case of pressing button 'Home', *
|
||||||
* go to top element */
|
* go to top element */
|
||||||
case Key.HOME:
|
case KEY.HOME:
|
||||||
DOM.setCurrentFile(Info.first);
|
DOM.setCurrentFile(Info.first);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* in case of pressing button 'End', select last element */
|
/* in case of pressing button 'End', select last element */
|
||||||
case Key.END:
|
case KEY.END:
|
||||||
DOM.setCurrentFile(Info.last);
|
DOM.setCurrentFile(Info.last);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* если нажали клавишу page down проматываем экран */
|
/* если нажали клавишу page down проматываем экран */
|
||||||
case Key.PAGE_DOWN:
|
case KEY.PAGE_DOWN:
|
||||||
DOM.scrollByPages(panel, 1);
|
DOM.scrollByPages(panel, 1);
|
||||||
|
|
||||||
for (i = 0; i < 30; i++) {
|
for (i = 0; i < 30; i++) {
|
||||||
|
|
@ -379,7 +393,7 @@ function KeyProto() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* если нажали клавишу page up проматываем экран */
|
/* если нажали клавишу page up проматываем экран */
|
||||||
case Key.PAGE_UP:
|
case KEY.PAGE_UP:
|
||||||
DOM.scrollByPages(panel, -1);
|
DOM.scrollByPages(panel, -1);
|
||||||
|
|
||||||
for (i = 0; i < 30; i++) {
|
for (i = 0; i < 30; i++) {
|
||||||
|
|
@ -393,26 +407,26 @@ function KeyProto() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.ENTER:
|
case KEY.ENTER:
|
||||||
if (Info.isDir)
|
if (Info.isDir)
|
||||||
await loadDir({path});
|
await changeDir(path);
|
||||||
else
|
else
|
||||||
CloudCmd.View.show();
|
CloudCmd.View.show();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.BACKSPACE:
|
case KEY.BACKSPACE:
|
||||||
CloudCmd.goToParentDir();
|
CloudCmd.goToParentDir();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.BACKSLASH:
|
case KEY.BACKSLASH:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
await loadDir({
|
await changeDir('/');
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.A:
|
case KEY.A:
|
||||||
if (ctrlMeta) {
|
if (ctrlMeta) {
|
||||||
DOM.selectAllFiles();
|
DOM.selectAllFiles();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -420,7 +434,7 @@ function KeyProto() {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.G:
|
case KEY.G:
|
||||||
if (alt) {
|
if (alt) {
|
||||||
DOM.goToDirectory();
|
DOM.goToDirectory();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -428,9 +442,17 @@ function KeyProto() {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.M:
|
case KEY.L:
|
||||||
|
if (ctrlMeta && shift) {
|
||||||
|
CloudCmd.logOut();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY.M:
|
||||||
if (ctrlMeta) {
|
if (ctrlMeta) {
|
||||||
if (shift)
|
if (config('vim'))
|
||||||
CloudCmd.EditNamesVim.show();
|
CloudCmd.EditNamesVim.show();
|
||||||
else
|
else
|
||||||
CloudCmd.EditNames.show();
|
CloudCmd.EditNames.show();
|
||||||
|
|
@ -440,7 +462,7 @@ function KeyProto() {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.P:
|
case KEY.P:
|
||||||
if (!ctrlMeta)
|
if (!ctrlMeta)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -450,6 +472,7 @@ function KeyProto() {
|
||||||
.catch(CloudCmd.log);
|
.catch(CloudCmd.log);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* обновляем страницу,
|
* обновляем страницу,
|
||||||
* загружаем содержимое каталога
|
* загружаем содержимое каталога
|
||||||
|
|
@ -457,44 +480,65 @@ function KeyProto() {
|
||||||
* сервера, а не из кэша
|
* сервера, а не из кэша
|
||||||
* (обновляем кэш)
|
* (обновляем кэш)
|
||||||
*/
|
*/
|
||||||
case Key.R:
|
case KEY.R:
|
||||||
if (ctrlMeta) {
|
if (ctrlMeta) {
|
||||||
CloudCmd.log('reloading page...\n');
|
CloudCmd.log('reloading page...\n');
|
||||||
CloudCmd.refresh();
|
CloudCmd.refresh();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.C:
|
case KEY.C:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
Buffer.copy();
|
Buffer.copy();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.X:
|
case KEY.X:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
Buffer.cut();
|
Buffer.cut();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.V:
|
case KEY.V:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
Buffer.paste();
|
Buffer.paste();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.Z:
|
case KEY.Z:
|
||||||
if (ctrlMeta)
|
if (ctrlMeta)
|
||||||
Buffer.clear();
|
Buffer.clear();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY.COLON:
|
||||||
|
CloudCmd.CommandLine.show();
|
||||||
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* чистим хранилище */
|
/* чистим хранилище */
|
||||||
case Key.D:
|
case KEY.D:
|
||||||
if (ctrlMeta) {
|
if (ctrlMeta) {
|
||||||
CloudCmd.log('clearing storage...');
|
CloudCmd.log('clearing storage...');
|
||||||
await DOM.Storage.clear();
|
await DOM.Storage.clear();
|
||||||
CloudCmd.log('storage cleared');
|
CloudCmd.log('storage cleared');
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY.DOT:
|
||||||
|
if (meta && shift) {
|
||||||
|
const showDotFiles = !CloudCmd.config('showDotFiles');
|
||||||
|
CloudCmd._config('showDotFiles', showDotFiles);
|
||||||
|
CloudCmd.refresh();
|
||||||
|
await DOM.RESTful.Config.write({
|
||||||
|
showDotFiles,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
56
client/key/index.spec.js
Normal file
56
client/key/index.spec.js
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import autoGlobals from 'auto-globals';
|
||||||
|
import supertape from 'supertape';
|
||||||
|
import {ESC} from './key.js';
|
||||||
|
import {Key, _listener} from './index.js';
|
||||||
|
import {getDOM, getCloudCmd} from './vim/globals.fixture.js';
|
||||||
|
|
||||||
|
const test = autoGlobals(supertape);
|
||||||
|
const {stub} = supertape;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: enable vim', async (t) => {
|
||||||
|
const vim = stub();
|
||||||
|
const config = stub().returns(true);
|
||||||
|
const _config = stub();
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
keyCode: ESC,
|
||||||
|
key: 'Escape',
|
||||||
|
altKey: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Key.setBind();
|
||||||
|
|
||||||
|
await _listener(event, {
|
||||||
|
vim,
|
||||||
|
config,
|
||||||
|
_config,
|
||||||
|
switchKey: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(vim, ['Escape', event]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: disable vim', async (t) => {
|
||||||
|
const _config = stub();
|
||||||
|
const config = stub();
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
keyCode: ESC,
|
||||||
|
key: 'Escape',
|
||||||
|
altKey: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Key.setBind();
|
||||||
|
await _listener(event, {
|
||||||
|
config,
|
||||||
|
_config,
|
||||||
|
switchKey: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(_config, ['vim', true]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,81 +1,58 @@
|
||||||
'use strict';
|
export const BACKSPACE = 8;
|
||||||
|
export const TAB = 9;
|
||||||
module.exports = {
|
export const ENTER = 13;
|
||||||
BACKSPACE : 8,
|
export const CAPSLOCK = 20;
|
||||||
TAB : 9,
|
export const ESC = 27;
|
||||||
ENTER : 13,
|
export const SPACE = 32;
|
||||||
ESC : 27,
|
export const PAGE_UP = 33;
|
||||||
|
export const PAGE_DOWN = 34;
|
||||||
SPACE : 32,
|
export const END = 35;
|
||||||
PAGE_UP : 33,
|
export const HOME = 36;
|
||||||
PAGE_DOWN : 34,
|
export const LEFT = 37;
|
||||||
END : 35,
|
export const UP = 38;
|
||||||
HOME : 36,
|
export const RIGHT = 39;
|
||||||
|
export const DOWN = 40;
|
||||||
LEFT : 37,
|
export const INSERT = 45;
|
||||||
UP : 38,
|
export const DELETE = 46;
|
||||||
RIGHT : 39,
|
export const ZERO = 48;
|
||||||
DOWN : 40,
|
export const SEMICOLON = 52;
|
||||||
|
export const A = 65;
|
||||||
INSERT : 45,
|
export const C = 67;
|
||||||
DELETE : 46,
|
export const D = 68;
|
||||||
|
export const G = 71;
|
||||||
ZERO : 48,
|
export const J = 74;
|
||||||
|
export const K = 75;
|
||||||
SEMICOLON : 52,
|
export const L = 76;
|
||||||
|
export const M = 77;
|
||||||
COLON : 54,
|
export const O = 79;
|
||||||
|
export const P = 80;
|
||||||
A : 65,
|
export const Q = 81;
|
||||||
|
export const R = 82;
|
||||||
C : 67,
|
export const S = 83;
|
||||||
D : 68,
|
export const T = 84;
|
||||||
|
export const U = 85;
|
||||||
G : 71,
|
export const V = 86;
|
||||||
|
export const X = 88;
|
||||||
J : 74,
|
export const Z = 90;
|
||||||
K : 75,
|
export const INSERT_MAC = 96;
|
||||||
|
export const ASTERISK = 106;
|
||||||
M : 77,
|
export const PLUS = 107;
|
||||||
|
export const MINUS = 109;
|
||||||
O : 79,
|
export const F1 = 112;
|
||||||
P : 80,
|
export const F2 = 113;
|
||||||
Q : 81,
|
export const F3 = 114;
|
||||||
R : 82,
|
export const F4 = 115;
|
||||||
S : 83,
|
export const F5 = 116;
|
||||||
T : 84,
|
export const F6 = 117;
|
||||||
U : 85,
|
export const F7 = 118;
|
||||||
|
export const F8 = 119;
|
||||||
V : 86,
|
export const F9 = 120;
|
||||||
|
export const F10 = 121;
|
||||||
X : 88,
|
export const COLON = 186;
|
||||||
|
export const EQUAL = 187;
|
||||||
Z : 90,
|
export const HYPHEN = 189;
|
||||||
|
export const DOT = 190;
|
||||||
INSERT_MAC : 96,
|
export const SLASH = 191;
|
||||||
|
export const TRA = 192;
|
||||||
ASTERISK : 106,
|
export const BACKSLASH = 220;
|
||||||
PLUS : 107,
|
export const BRACKET_CLOSE = 221;
|
||||||
MINUS : 109,
|
|
||||||
|
|
||||||
F1 : 112,
|
|
||||||
F2 : 113,
|
|
||||||
F3 : 114,
|
|
||||||
F4 : 115,
|
|
||||||
F5 : 116,
|
|
||||||
F6 : 117,
|
|
||||||
F7 : 118,
|
|
||||||
F8 : 119,
|
|
||||||
F9 : 120,
|
|
||||||
F10 : 121,
|
|
||||||
|
|
||||||
EQUAL : 187,
|
|
||||||
HYPHEN : 189,
|
|
||||||
DOT : 190,
|
|
||||||
SLASH : 191,
|
|
||||||
TRA : 192, /* Typewritten Reverse Apostrophe (`) */
|
|
||||||
BACKSLASH : 220,
|
|
||||||
|
|
||||||
BRACKET_CLOSE: 221,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
import {escapeRegExp} from '#common/util';
|
||||||
|
|
||||||
'use strict';
|
export default function setCurrentByChar(char, charStore) {
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
const {escapeRegExp} = require('../../common/util');
|
|
||||||
|
|
||||||
module.exports = function setCurrentByChar(char, charStore) {
|
|
||||||
let firstByName;
|
let firstByName;
|
||||||
let skipCount = 0;
|
let skipCount = 0;
|
||||||
let setted = false;
|
let set = false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
const escapeChar = escapeRegExp(char);
|
const escapeChar = escapeRegExp(char);
|
||||||
const regExp = new RegExp('^' + escapeChar + '.*$', 'i');
|
const regExp = new RegExp(`^${escapeChar}.*$`, 'i');
|
||||||
const {files} = Info;
|
const {files} = Info;
|
||||||
const chars = charStore();
|
const chars = charStore();
|
||||||
const n = chars.length;
|
const n = chars.length;
|
||||||
|
|
@ -31,12 +28,14 @@ module.exports = function setCurrentByChar(char, charStore) {
|
||||||
const isTest = (a) => regExp.test(a);
|
const isTest = (a) => regExp.test(a);
|
||||||
const isRoot = (a) => a === '..';
|
const isRoot = (a) => a === '..';
|
||||||
const not = (f) => (a) => !f(a);
|
const not = (f) => (a) => !f(a);
|
||||||
|
|
||||||
const setCurrent = (name) => {
|
const setCurrent = (name) => {
|
||||||
const byName = DOM.getCurrentByName(name);
|
const byName = DOM.getCurrentByName(name);
|
||||||
|
|
||||||
if (!skipCount) {
|
if (!skipCount) {
|
||||||
setted = true;
|
set = true;
|
||||||
DOM.setCurrentFile(byName);
|
DOM.setCurrentFile(byName);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,9 +50,8 @@ module.exports = function setCurrentByChar(char, charStore) {
|
||||||
.filter(not(isRoot))
|
.filter(not(isRoot))
|
||||||
.some(setCurrent);
|
.some(setCurrent);
|
||||||
|
|
||||||
if (!setted) {
|
if (!set) {
|
||||||
DOM.setCurrentFile(firstByName);
|
DOM.setCurrentFile(firstByName);
|
||||||
charStore([char]);
|
charStore([char]);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
'use strict';
|
import {fullstore} from 'fullstore';
|
||||||
|
import limier from 'limier';
|
||||||
const fullstore = require('fullstore');
|
|
||||||
const limier = require('limier');
|
|
||||||
|
|
||||||
const searchStore = fullstore([]);
|
const searchStore = fullstore([]);
|
||||||
const searchIndex = fullstore(0);
|
const searchIndex = fullstore(0);
|
||||||
|
|
||||||
module.exports.find = (value, names) => {
|
export const find = (value, names) => {
|
||||||
const result = limier(value, names);
|
const result = limier(value, names);
|
||||||
|
|
||||||
searchStore(result);
|
searchStore(result);
|
||||||
|
|
@ -15,7 +13,7 @@ module.exports.find = (value, names) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.findNext = () => {
|
export const findNext = () => {
|
||||||
const names = searchStore();
|
const names = searchStore();
|
||||||
const index = next(searchIndex(), names.length);
|
const index = next(searchIndex(), names.length);
|
||||||
|
|
||||||
|
|
@ -23,7 +21,7 @@ module.exports.findNext = () => {
|
||||||
return names[searchIndex()];
|
return names[searchIndex()];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.findPrevious = () => {
|
export const findPrevious = () => {
|
||||||
const names = searchStore();
|
const names = searchStore();
|
||||||
const index = previous(searchIndex(), names.length);
|
const index = previous(searchIndex(), names.length);
|
||||||
|
|
||||||
|
|
@ -31,8 +29,8 @@ module.exports.findPrevious = () => {
|
||||||
return names[index];
|
return names[index];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports._next = next;
|
export const _next = next;
|
||||||
module.exports._previous = previous;
|
export const _previous = previous;
|
||||||
|
|
||||||
function next(index, length) {
|
function next(index, length) {
|
||||||
if (index === length - 1)
|
if (index === length - 1)
|
||||||
|
|
@ -47,4 +45,3 @@ function previous(index, length) {
|
||||||
|
|
||||||
return --index;
|
return --index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
'use strict';
|
import test from 'supertape';
|
||||||
|
import {getDOM} from './globals.fixture.js';
|
||||||
const test = require('supertape');
|
import {
|
||||||
const dir = './';
|
|
||||||
|
|
||||||
const {getDOM} = require('./globals.fixture');
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
|
||||||
|
|
||||||
const {
|
|
||||||
_next,
|
_next,
|
||||||
_previous,
|
_previous,
|
||||||
} = require(dir + 'find');
|
find,
|
||||||
|
findNext,
|
||||||
|
findPrevious,
|
||||||
|
} from './find.js';
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _next', (t) => {
|
test('cloudcmd: client: vim: _next', (t) => {
|
||||||
const result = _next(1, 2);
|
const result = _next(1, 2);
|
||||||
|
|
@ -19,6 +17,13 @@ test('cloudcmd: client: vim: _next', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: _next: increment', (t) => {
|
||||||
|
const result = _next(0, 2);
|
||||||
|
|
||||||
|
t.equal(result, 1, 'should return 1');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _previous', (t) => {
|
test('cloudcmd: client: vim: _previous', (t) => {
|
||||||
const result = _previous(0, 2);
|
const result = _previous(0, 2);
|
||||||
|
|
||||||
|
|
@ -26,3 +31,25 @@ test('cloudcmd: client: vim: _previous', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: _previous: decrement', (t) => {
|
||||||
|
const result = _previous(1, 2);
|
||||||
|
|
||||||
|
t.equal(result, 0, 'should return 0');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: findNext: after find', (t) => {
|
||||||
|
find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
const result = findNext();
|
||||||
|
|
||||||
|
t.equal(result, 'beta', 'should return next found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: findPrevious: after find', (t) => {
|
||||||
|
find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
const result = findPrevious();
|
||||||
|
|
||||||
|
t.equal(result, 'apple', 'should return previous found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
'use strict';
|
const noop = () => {};
|
||||||
|
|
||||||
module.exports.getDOM = () => {
|
export const getDOM = () => {
|
||||||
const prompt = Promise.resolve.bind(Promise);
|
const prompt = Promise.resolve.bind(Promise);
|
||||||
const CurrentInfo = {
|
const CurrentInfo = {
|
||||||
element: {},
|
element: {},
|
||||||
files: [],
|
files: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const noop = () => {};
|
|
||||||
const Buffer = {
|
const Buffer = {
|
||||||
copy: noop,
|
copy: noop,
|
||||||
paste: noop,
|
paste: noop,
|
||||||
|
|
@ -28,16 +27,20 @@ module.exports.getDOM = () => {
|
||||||
getCurrentName: noop,
|
getCurrentName: noop,
|
||||||
setCurrentByName: noop,
|
setCurrentByName: noop,
|
||||||
toggleSelectedFile: noop,
|
toggleSelectedFile: noop,
|
||||||
|
prompNewDirectory: noop,
|
||||||
|
promptNewFile: noop,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.getCloudCmd = () => {
|
export const getCloudCmd = () => {
|
||||||
const show = () => {};
|
const show = () => {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Operation: {
|
Operation: {
|
||||||
show,
|
show,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
config: noop,
|
||||||
|
_config: noop,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,167 @@
|
||||||
'use strict';
|
import vim from './vim.js';
|
||||||
|
import * as finder from './find.js';
|
||||||
|
import {
|
||||||
|
setCurrent,
|
||||||
|
selectFileNotParent,
|
||||||
|
} from './set-current.js';
|
||||||
|
|
||||||
/* global CloudCmd */
|
export default (key, event, overrides = {}) => {
|
||||||
/* global DOM */
|
const defaults = {
|
||||||
|
...globalThis.DOM,
|
||||||
const vim = require('./vim');
|
...globalThis.CloudCmd,
|
||||||
const finder = require('./find');
|
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const {Dialog} = DOM;
|
|
||||||
|
|
||||||
module.exports = async (key, event) => {
|
|
||||||
const operations = getOperations(event);
|
|
||||||
await vim(key, operations);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOperations = (event) => {
|
const deps = {
|
||||||
|
...defaults,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
|
||||||
|
const operations = getOperations(event, deps);
|
||||||
|
|
||||||
|
vim(key, operations, deps);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOperations = (event, deps) => {
|
||||||
|
const {
|
||||||
|
Info = globalThis.DOM.CurrentInfo,
|
||||||
|
CloudCmd = globalThis.CloudCmd,
|
||||||
|
Operation,
|
||||||
|
unselectFiles,
|
||||||
|
setCurrentFile,
|
||||||
|
setCurrentByName,
|
||||||
|
getCurrentName,
|
||||||
|
prompt = globalThis.DOM.Dialog.prompt,
|
||||||
|
preventDefault = event?.preventDefault?.bind(event),
|
||||||
|
stopImmediatePropagation = event?.preventDefault?.bind(event),
|
||||||
|
promptNewFile = globalThis.DOM.promptNewFile,
|
||||||
|
toggleSelectedFile,
|
||||||
|
Buffer = {},
|
||||||
|
createFindNext = _createFindNext,
|
||||||
|
createFindPrevious = _createFindPrevious,
|
||||||
|
createMakeFile = _createMakeFile,
|
||||||
|
renameCurrent,
|
||||||
|
} = deps;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
escape: DOM.unselectFiles,
|
makeFile: createMakeFile({
|
||||||
|
promptNewFile,
|
||||||
|
preventDefault,
|
||||||
|
stopImmediatePropagation,
|
||||||
|
}),
|
||||||
|
findNext: createFindNext({
|
||||||
|
setCurrentByName,
|
||||||
|
}),
|
||||||
|
findPrevious: createFindPrevious({
|
||||||
|
setCurrentByName,
|
||||||
|
}),
|
||||||
|
escape: unselectFiles,
|
||||||
|
rename: () => {
|
||||||
|
event.preventDefault();
|
||||||
|
renameCurrent();
|
||||||
|
},
|
||||||
remove: () => {
|
remove: () => {
|
||||||
CloudCmd.Operation.show('delete');
|
Operation.show('delete');
|
||||||
},
|
},
|
||||||
|
operationCopy: () => {
|
||||||
|
event.preventDefault();
|
||||||
|
Operation.show('copy');
|
||||||
|
},
|
||||||
|
operationMove: () => {
|
||||||
|
event.preventDefault();
|
||||||
|
Operation.show('move');
|
||||||
|
},
|
||||||
|
|
||||||
|
makeDirectory: () => {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
globalThis.DOM.promptNewDir();
|
||||||
|
},
|
||||||
|
|
||||||
|
terminal: () => {
|
||||||
|
CloudCmd.Terminal.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
edit: () => {
|
||||||
|
CloudCmd.EditFileVim.show();
|
||||||
|
},
|
||||||
|
|
||||||
copy: () => {
|
copy: () => {
|
||||||
DOM.Buffer.copy();
|
Buffer.copy();
|
||||||
DOM.unselectFiles();
|
unselectFiles();
|
||||||
},
|
},
|
||||||
|
|
||||||
select: () => {
|
select: () => {
|
||||||
const current = Info.element;
|
const current = Info.element;
|
||||||
DOM.toggleSelectedFile(current);
|
toggleSelectedFile(current);
|
||||||
},
|
},
|
||||||
paste: DOM.Buffer.paste,
|
|
||||||
|
paste: Buffer.paste,
|
||||||
|
|
||||||
moveNext: ({count, isVisual, isDelete}) => {
|
moveNext: ({count, isVisual, isDelete}) => {
|
||||||
setCurrent('next', {
|
setCurrent('next', {
|
||||||
count,
|
count,
|
||||||
isVisual,
|
isVisual,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
Operation,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
movePrevious: ({count, isVisual, isDelete}) => {
|
movePrevious: ({count, isVisual, isDelete}) => {
|
||||||
setCurrent('previous', {
|
setCurrent('previous', {
|
||||||
count,
|
count,
|
||||||
isVisual,
|
isVisual,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
Operation,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
find: async () => {
|
find: async () => {
|
||||||
event.preventDefault();
|
preventDefault();
|
||||||
const [, value] = await Dialog.prompt('Find', '');
|
const [, value] = await prompt('Find', '');
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const names = Info.files.map(DOM.getCurrentName);
|
const names = Info.files.map(getCurrentName);
|
||||||
const [result] = finder.find(value, names);
|
const [result] = finder.find(value, names);
|
||||||
|
|
||||||
DOM.setCurrentByName(result);
|
setCurrentByName(result);
|
||||||
},
|
},
|
||||||
findNext: () => {
|
};
|
||||||
const name = finder.findNext();
|
};
|
||||||
DOM.setCurrentByName(name);
|
|
||||||
},
|
export const selectFile = selectFileNotParent;
|
||||||
findPrevious: () => {
|
|
||||||
|
const _createFindPrevious = (overrides = {}) => () => {
|
||||||
|
const {setCurrentByName} = overrides;
|
||||||
const name = finder.findPrevious();
|
const name = finder.findPrevious();
|
||||||
DOM.setCurrentByName(name);
|
|
||||||
},
|
setCurrentByName(name);
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.selectFile = selectFile;
|
const _createFindNext = (overrides = {}) => () => {
|
||||||
|
const {setCurrentByName} = overrides;
|
||||||
|
const name = finder.findNext();
|
||||||
|
|
||||||
function selectFile(current) {
|
setCurrentByName(name);
|
||||||
const name = DOM.getCurrentName(current);
|
};
|
||||||
|
|
||||||
if (name === '..')
|
const _createMakeFile = (overrides = {}) => () => {
|
||||||
return;
|
const {
|
||||||
|
promptNewFile,
|
||||||
DOM.selectFile(current);
|
stopImmediatePropagation,
|
||||||
}
|
preventDefault,
|
||||||
|
} = overrides;
|
||||||
function setCurrent(sibling, {count, isVisual, isDelete}) {
|
|
||||||
let current = Info.element;
|
|
||||||
const select = isVisual ? selectFile : DOM.unselectFile;
|
|
||||||
|
|
||||||
select(current);
|
|
||||||
|
|
||||||
const position = `${sibling}Sibling`;
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const next = current[position];
|
|
||||||
|
|
||||||
if (!next)
|
|
||||||
break;
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
select(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
DOM.setCurrentFile(current);
|
|
||||||
|
|
||||||
if (isDelete)
|
|
||||||
CloudCmd.Operation.show('delete');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
stopImmediatePropagation();
|
||||||
|
preventDefault();
|
||||||
|
promptNewFile();
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,35 @@
|
||||||
'use strict';
|
import {test, stub} from 'supertape';
|
||||||
|
import {getDOM, getCloudCmd} from './globals.fixture.js';
|
||||||
|
import vim, {selectFile as vimSelectFile} from './index.js';
|
||||||
|
import * as finder from './find.js';
|
||||||
|
|
||||||
const {join} = require('path');
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const test = require('supertape');
|
const {assign} = Object;
|
||||||
const stub = require('@cloudcmd/stub');
|
const {DOM} = globalThis;
|
||||||
const mockRequire = require('mock-require');
|
|
||||||
const {reRequire, stopAll} = mockRequire;
|
|
||||||
|
|
||||||
const dir = '../';
|
|
||||||
|
|
||||||
const pathVim = join(dir, 'vim');
|
|
||||||
const pathFind = join(dir, 'vim', 'find');
|
|
||||||
|
|
||||||
const {
|
|
||||||
getDOM,
|
|
||||||
getCloudCmd,
|
|
||||||
} = require('./globals.fixture');
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
|
||||||
global.CloudCmd = getCloudCmd();
|
|
||||||
|
|
||||||
const {DOM} = global;
|
|
||||||
const {Buffer} = DOM;
|
const {Buffer} = DOM;
|
||||||
|
|
||||||
const vim = require(pathVim);
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file: no', (t) => {
|
test('cloudcmd: client: key: set next file: no', (t) => {
|
||||||
const element = {
|
const element = {};
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
vim('j', {}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
t.calledWith(setCurrentFile, [element], 'should set next file');
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
|
||||||
|
|
||||||
vim('j', {});
|
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(element), 'should set next file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file current', (t) => {
|
test('cloudcmd: client: key: set next file current: j', async (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
|
|
@ -48,17 +37,21 @@ test('cloudcmd: client: key: set next file current', (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('j', {});
|
await vim('j', {}, {
|
||||||
|
Info,
|
||||||
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file current', (t) => {
|
test('cloudcmd: client: key: set next file current: mjj', (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
|
|
@ -66,15 +59,21 @@ test('cloudcmd: client: key: set next file current', (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('m', {});
|
const deps = {
|
||||||
vim('j', {});
|
Info,
|
||||||
vim('j', {});
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
vim('m', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -86,38 +85,44 @@ test('cloudcmd: client: key: set next file current: g', (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('g', {});
|
const deps = {
|
||||||
vim('j', {});
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(nextSibling), 'should ignore g');
|
vim('g', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should ignore g');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set +2 file current', (t) => {
|
test('cloudcmd: client: key: set +2 file current', (t) => {
|
||||||
const last = {};
|
const last = {};
|
||||||
const nextSibling = {
|
|
||||||
nextSibling: last,
|
|
||||||
};
|
|
||||||
const element = {
|
|
||||||
nextSibling,
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
const element = {};
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
setCurrentFile,
|
||||||
|
Info,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('2', event);
|
vim('2', event, deps);
|
||||||
vim('j', event);
|
vim('j', event, deps);
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(last), 'should set next file');
|
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -126,25 +131,39 @@ test('cloudcmd: client: key: select +2 files from current before delete', (t) =>
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
global.DOM.selectFile = stub();
|
};
|
||||||
global.DOM.getCurrentName = () => false;
|
|
||||||
global.CloudCmd.Operation.show = stub();
|
const Operation = {
|
||||||
|
show: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectFile = stub();
|
||||||
|
const getCurrentName = stub().returns('x');
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('d', event);
|
const deps = {
|
||||||
vim('2', event);
|
Info,
|
||||||
vim('j', event);
|
setCurrentFile,
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
Operation,
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(last), 'should set next file');
|
vim('d', event, deps);
|
||||||
|
vim('2', event, deps);
|
||||||
|
vim('j', event, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -153,6 +172,7 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
@ -160,20 +180,26 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
const show = stub();
|
const show = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const deps = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
Info: {
|
||||||
global.DOM.selectFile = stub();
|
element,
|
||||||
global.DOM.getCurrentName = () => false;
|
},
|
||||||
global.CloudCmd.Operation.show = show;
|
Operation: {
|
||||||
|
show,
|
||||||
|
},
|
||||||
|
setCurrentFile,
|
||||||
|
selectFile: stub(),
|
||||||
|
getCurrentName: stub().returns('x'),
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('d', event);
|
vim('d', event, deps);
|
||||||
vim('2', event);
|
vim('2', event, deps);
|
||||||
vim('j', event);
|
vim('j', event, deps);
|
||||||
|
|
||||||
t.ok(show.calledWith('delete'), 'should call delete');
|
|
||||||
|
|
||||||
|
t.calledWith(show, ['delete'], 'should call delete');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -184,49 +210,84 @@ test('cloudcmd: client: key: set previous file current', (t) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('k', {});
|
const deps = {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(previousSibling), 'should set previous file');
|
vim('k', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [previousSibling], 'should set previous file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy: no', (t) => {
|
test('cloudcmd: client: key: copy: no', (t) => {
|
||||||
const copy = stub();
|
const copy = stub();
|
||||||
|
|
||||||
Buffer.copy = copy;
|
vim('y', {}, {
|
||||||
|
unselectFiles: stub(),
|
||||||
vim('y', {});
|
Buffer: {
|
||||||
|
copy,
|
||||||
t.notOk(copy.called, 'should not copy files');
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.notCalled(copy, 'should not copy files');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy', (t) => {
|
test('cloudcmd: client: key: copy', (t) => {
|
||||||
const copy = stub();
|
const copy = stub();
|
||||||
|
const Info = {
|
||||||
|
element: {},
|
||||||
|
};
|
||||||
|
|
||||||
Buffer.copy = copy;
|
const toggleSelectedFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
vim('v', {});
|
const deps = {
|
||||||
vim('y', {});
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
toggleSelectedFile,
|
||||||
|
Buffer: {
|
||||||
|
copy,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(copy.calledWith(), 'should copy files');
|
vim('v', {}, deps);
|
||||||
|
vim('y', {}, deps);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(copy, 'should copy files');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
|
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
|
||||||
const unselectFiles = stub();
|
const unselectFiles = stub();
|
||||||
|
const Info = {
|
||||||
|
element: {},
|
||||||
|
};
|
||||||
|
|
||||||
DOM.unselectFiles = unselectFiles;
|
const toggleSelectedFile = stub();
|
||||||
|
|
||||||
vim('v', {});
|
const deps = {
|
||||||
vim('y', {});
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
toggleSelectedFile,
|
||||||
|
Buffer: {
|
||||||
|
copy: stub(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
t.ok(unselectFiles.calledWith(), 'should unselect files');
|
vim('v', {}, deps);
|
||||||
|
vim('y', {}, deps);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(unselectFiles, 'should unselect files');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -235,63 +296,91 @@ test('cloudcmd: client: key: paste', (t) => {
|
||||||
|
|
||||||
Buffer.paste = paste;
|
Buffer.paste = paste;
|
||||||
|
|
||||||
vim('p', {});
|
vim('p', {}, {
|
||||||
|
Buffer,
|
||||||
t.ok(paste.calledWith(), 'should paste files');
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(paste, 'should paste files');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: selectFile: ..', (t) => {
|
test('cloudcmd: client: key: selectFile: ..', (t) => {
|
||||||
|
const getCurrentName = stub().returns('..');
|
||||||
const selectFile = stub();
|
const selectFile = stub();
|
||||||
const getCurrentName = stub();
|
|
||||||
|
|
||||||
DOM.selectFile = selectFile;
|
|
||||||
DOM.getCurrentName = () => '..';
|
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
vim.selectFile(current);
|
|
||||||
|
|
||||||
t.notOk(getCurrentName.called, 'should not call selectFile');
|
vimSelectFile(current, {
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.notCalled(selectFile, 'should not call selectFile');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: selectFile', (t) => {
|
test('cloudcmd: client: key: selectFile', (t) => {
|
||||||
const selectFile = stub();
|
const selectFile = stub();
|
||||||
|
const getCurrentName = stub().returns('x');
|
||||||
DOM.selectFile = selectFile;
|
|
||||||
DOM.getCurrentName = (a) => a.name;
|
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
|
|
||||||
vim.selectFile(current);
|
vimSelectFile(current, {
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
});
|
||||||
|
|
||||||
t.ok(selectFile.calledWith(current), 'should call selectFile');
|
t.calledWith(selectFile, [current], 'should call selectFile');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set last file current', (t) => {
|
test('cloudcmd: client: key: set last file current: shift + g', async (t) => {
|
||||||
const last = 'last';
|
const last = 'last';
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
await vim('G', {}, {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
Info: {
|
||||||
|
element,
|
||||||
vim('G', {});
|
},
|
||||||
|
setCurrentFile,
|
||||||
t.ok(setCurrentFile.calledWith(last), 'should set last file');
|
unselectFiles: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set last file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set first file current', (t) => {
|
test('cloudcmd: client: key: set last file current: $', (t) => {
|
||||||
|
const last = 'last';
|
||||||
|
const nextSibling = {
|
||||||
|
nextSibling: last,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
vim('$', {}, {
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set last file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set first file current: gg', (t) => {
|
||||||
const first = 'first';
|
const first = 'first';
|
||||||
const previousSibling = {
|
const previousSibling = {
|
||||||
previousSibling: first,
|
previousSibling: first,
|
||||||
|
|
@ -301,66 +390,121 @@ test('cloudcmd: client: key: set first file current', (t) => {
|
||||||
previousSibling,
|
previousSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const unselectFiles = stub();
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const deps = {
|
||||||
global.DOM.setCurrentFile = setCurrentFile;
|
Operation,
|
||||||
|
unselectFiles,
|
||||||
|
setCurrentFile,
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
vim('g', {});
|
vim('g', {}, deps);
|
||||||
vim('g', {});
|
vim('g', {}, deps);
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(first), 'should set first file');
|
t.calledWith(setCurrentFile, [first], 'should set first file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set first file current: ^', async (t) => {
|
||||||
|
const first = 'first';
|
||||||
|
const previousSibling = {
|
||||||
|
previousSibling: first,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
previousSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const unselectFiles = stub();
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
setCurrentFile,
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
unselectFiles,
|
||||||
|
Operation,
|
||||||
|
};
|
||||||
|
|
||||||
|
await vim('^', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [first], 'should set first file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: visual', (t) => {
|
test('cloudcmd: client: key: visual', (t) => {
|
||||||
const element = {
|
const element = {};
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSelectedFile = stub();
|
const toggleSelectedFile = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.toggleSelectedFile = toggleSelectedFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('v', {});
|
vim('v', {}, {
|
||||||
|
Info,
|
||||||
t.ok(toggleSelectedFile.calledWith(element), 'should toggle selection');
|
toggleSelectedFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(toggleSelectedFile, [element], 'should toggle selection');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: ESC', (t) => {
|
test('cloudcmd: client: key: ESC', (t) => {
|
||||||
const element = {
|
const element = {};
|
||||||
};
|
|
||||||
|
|
||||||
const unselectFiles = stub();
|
const unselectFiles = stub();
|
||||||
|
|
||||||
global.DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
global.DOM.unselectFiles = unselectFiles ;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('Escape');
|
vim('Escape', null, {
|
||||||
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
t.ok(unselectFiles.calledWith(), 'should toggle selection');
|
t.calledWithNoArgs(unselectFiles, 'should toggle selection');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: Enter', (t) => {
|
test('cloudcmd: client: key: Enter', async (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unselectFiles = stub();
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
DOM.setCurrentFile = setCurrentFile;
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
vim('Enter');
|
await vim('Enter', null, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
vim('j');
|
await vim('j', null, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -368,50 +512,245 @@ test('cloudcmd: client: key: /', (t) => {
|
||||||
const preventDefault = stub();
|
const preventDefault = stub();
|
||||||
const element = {};
|
const element = {};
|
||||||
|
|
||||||
DOM.CurrentInfo.element = element;
|
const Info = {
|
||||||
DOM.getCurrentName = () => '';
|
element,
|
||||||
|
files: [],
|
||||||
|
};
|
||||||
|
|
||||||
vim('/', {
|
const getCurrentName = stub().returns('');
|
||||||
|
|
||||||
|
const event = {
|
||||||
preventDefault,
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
const prompt = stub().returns([]);
|
||||||
|
|
||||||
|
vim('/', event, {
|
||||||
|
getCurrentName,
|
||||||
|
Info,
|
||||||
|
prompt,
|
||||||
});
|
});
|
||||||
|
|
||||||
t.ok(preventDefault.calledWith(), 'should call preventDefault');
|
t.calledWithNoArgs(preventDefault);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: find', (t) => {
|
||||||
|
assign(DOM.Dialog, {
|
||||||
|
prompt: stub().returns([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const setCurrentByName = stub();
|
||||||
|
|
||||||
|
assign(DOM, {
|
||||||
|
setCurrentByName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('/', event);
|
||||||
|
|
||||||
|
t.notCalled(setCurrentByName);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: n', (t) => {
|
test('cloudcmd: client: key: n', (t) => {
|
||||||
const findNext = stub();
|
const findNext = stub();
|
||||||
|
const createFindNext = stub().returns(findNext);
|
||||||
|
|
||||||
mockRequire(pathFind, {
|
|
||||||
findNext,
|
|
||||||
});
|
|
||||||
|
|
||||||
const vim = reRequire(pathVim);
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('n', event);
|
vim('n', event, {
|
||||||
|
createFindNext,
|
||||||
|
});
|
||||||
|
|
||||||
stopAll(pathFind);
|
t.calledWithNoArgs(findNext, 'should call findNext');
|
||||||
|
|
||||||
t.ok(findNext.calledWith(), 'should call findNext');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: N', (t) => {
|
test('cloudcmd: client: key: N', (t) => {
|
||||||
const findPrevious = stub();
|
const findPrevious = stub();
|
||||||
|
const createFindPrevious = stub().returns(findPrevious);
|
||||||
mockRequire(pathFind, {
|
|
||||||
findPrevious,
|
|
||||||
});
|
|
||||||
|
|
||||||
const vim = reRequire(dir + 'vim');
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('N', event);
|
vim('N', event, {
|
||||||
|
createFindPrevious,
|
||||||
|
});
|
||||||
|
|
||||||
stopAll(pathFind);
|
t.calledWithNoArgs(findPrevious);
|
||||||
|
|
||||||
t.ok(findPrevious.calledWith(), 'should call findPrevious');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: make directory', async (t) => {
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
|
||||||
|
assign(DOM, {
|
||||||
|
promptNewDir: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
stopImmediatePropagation: stub(),
|
||||||
|
preventDefault: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await vim('m', event);
|
||||||
|
await vim('d', event);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(DOM.promptNewDir);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: make file', (t) => {
|
||||||
|
const promptNewFile = stub();
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
stopImmediatePropagation: stub(),
|
||||||
|
preventDefault: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('m', event);
|
||||||
|
vim('f', event, {
|
||||||
|
promptNewFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(promptNewFile);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: terminal', (t) => {
|
||||||
|
const CloudCmd = {
|
||||||
|
Terminal: {
|
||||||
|
show: stub(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('t', event, {
|
||||||
|
CloudCmd,
|
||||||
|
});
|
||||||
|
vim('t', event, {
|
||||||
|
CloudCmd,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(CloudCmd.Terminal.show);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: edit', async (t) => {
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const {CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
assign(CloudCmd, {
|
||||||
|
EditFileVim: {
|
||||||
|
show: stub(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
await vim('e', event);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(CloudCmd.EditFileVim.show);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: rename', async (t) => {
|
||||||
|
const DOM = getDOM();
|
||||||
|
const renameCurrent = stub();
|
||||||
|
|
||||||
|
assign(DOM, {
|
||||||
|
renameCurrent,
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await vim('rr', event, DOM);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(renameCurrent);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: cc: operationCopy', (t) => {
|
||||||
|
const show = stub();
|
||||||
|
const preventDefault = stub();
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('c', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
vim('c', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(show, ['copy'], 'should show copy operation');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: mm: operationMove', (t) => {
|
||||||
|
const show = stub();
|
||||||
|
const preventDefault = stub();
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('m', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
vim('m', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(show, ['move'], 'should show move operation');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: n: findNext: real', (t) => {
|
||||||
|
const setCurrentByName = stub();
|
||||||
|
|
||||||
|
finder.find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('n', event, {
|
||||||
|
setCurrentByName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentByName, ['beta'], 'should set current by next found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: N: findPrevious: real', (t) => {
|
||||||
|
const setCurrentByName = stub();
|
||||||
|
|
||||||
|
finder.find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('N', event, {
|
||||||
|
setCurrentByName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentByName, ['apple'], 'should set current by previous found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
33
client/key/vim/set-current.js
Normal file
33
client/key/vim/set-current.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* global DOM */
|
||||||
|
export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) {
|
||||||
|
const name = getCurrentName(current);
|
||||||
|
|
||||||
|
if (name === '..')
|
||||||
|
return;
|
||||||
|
|
||||||
|
selectFile(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => {
|
||||||
|
let current = Info.element;
|
||||||
|
const select = isVisual ? selectFileNotParent : unselectFiles;
|
||||||
|
|
||||||
|
select(current);
|
||||||
|
|
||||||
|
const position = `${sibling}Sibling`;
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const next = current[position];
|
||||||
|
|
||||||
|
if (!next)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
select(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentFile(current);
|
||||||
|
|
||||||
|
if (isDelete)
|
||||||
|
Operation.show('delete');
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
'use strict';
|
import {fullstore} from 'fullstore';
|
||||||
|
|
||||||
const fullstore = require('fullstore');
|
|
||||||
const store = fullstore('');
|
const store = fullstore('');
|
||||||
const visual = fullstore(false);
|
const visual = fullstore(false);
|
||||||
|
|
||||||
|
|
@ -21,10 +20,11 @@ const rmFirst = (a) => {
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
module.exports = (key, operations) => {
|
export default (key, operations = {}) => {
|
||||||
const prevStore = store();
|
const prevStore = store();
|
||||||
const isVisual = visual();
|
const isVisual = visual();
|
||||||
const value = store(prevStore.concat(key));
|
const value = store(prevStore.concat(key));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
escape = noop,
|
escape = noop,
|
||||||
moveNext = noop,
|
moveNext = noop,
|
||||||
|
|
@ -36,6 +36,13 @@ module.exports = (key, operations) => {
|
||||||
find = noop,
|
find = noop,
|
||||||
findNext = noop,
|
findNext = noop,
|
||||||
findPrevious = noop,
|
findPrevious = noop,
|
||||||
|
makeFile = noop,
|
||||||
|
makeDirectory = noop,
|
||||||
|
terminal = noop,
|
||||||
|
edit = noop,
|
||||||
|
operationCopy = noop,
|
||||||
|
operationMove = noop,
|
||||||
|
rename = noop,
|
||||||
} = operations;
|
} = operations;
|
||||||
|
|
||||||
if (key === 'Enter')
|
if (key === 'Enter')
|
||||||
|
|
@ -44,10 +51,11 @@ module.exports = (key, operations) => {
|
||||||
if (key === 'Escape') {
|
if (key === 'Escape') {
|
||||||
visual(false);
|
visual(false);
|
||||||
escape();
|
escape();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'j') {
|
if (key === 'j' || key === 'w') {
|
||||||
const {
|
const {
|
||||||
count,
|
count,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
|
@ -63,7 +71,7 @@ module.exports = (key, operations) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'k') {
|
if (key === 'k' || key === 'b') {
|
||||||
const {
|
const {
|
||||||
count,
|
count,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
|
@ -79,11 +87,8 @@ module.exports = (key, operations) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^gg$/.test(value)) {
|
if (value === 'gg' || key === '^') {
|
||||||
const {
|
const {isDelete, isVisual} = handleDelete(prevStore);
|
||||||
isDelete,
|
|
||||||
isVisual,
|
|
||||||
} = handleDelete(prevStore);
|
|
||||||
|
|
||||||
movePrevious({
|
movePrevious({
|
||||||
count: Infinity,
|
count: Infinity,
|
||||||
|
|
@ -94,13 +99,49 @@ module.exports = (key, operations) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'd' && (visual() || prevStore === 'd')) {
|
if (value === 'md') {
|
||||||
stopVisual();
|
makeDirectory();
|
||||||
remove();
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'G') {
|
if (value === 'tt') {
|
||||||
|
terminal();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'e') {
|
||||||
|
edit();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'cc') {
|
||||||
|
operationCopy();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'mm') {
|
||||||
|
operationMove();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'mf') {
|
||||||
|
makeFile();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'rr') {
|
||||||
|
rename();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'd' && (visual() || prevStore === 'd')) {
|
||||||
|
stopVisual();
|
||||||
|
remove();
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'G' || key === '$') {
|
||||||
moveNext({
|
moveNext({
|
||||||
count: Infinity,
|
count: Infinity,
|
||||||
isVisual,
|
isVisual,
|
||||||
|
|
@ -115,6 +156,7 @@ module.exports = (key, operations) => {
|
||||||
|
|
||||||
stopVisual();
|
stopVisual();
|
||||||
copy();
|
copy();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +168,7 @@ module.exports = (key, operations) => {
|
||||||
if (/^v$/i.test(key)) {
|
if (/^v$/i.test(key)) {
|
||||||
visual(!visual());
|
visual(!visual());
|
||||||
select();
|
select();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,6 +186,9 @@ module.exports = (key, operations) => {
|
||||||
findPrevious();
|
findPrevious();
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key === ' ')
|
||||||
|
return end();
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleDelete(prevStore) {
|
function handleDelete(prevStore) {
|
||||||
|
|
@ -172,4 +218,3 @@ function getNumber(value) {
|
||||||
|
|
||||||
return parseInt(value);
|
return parseInt(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
'use strict';
|
import {test, stub} from 'supertape';
|
||||||
|
import vim from './vim.js';
|
||||||
const test = require('supertape');
|
|
||||||
const vim = require('./vim');
|
|
||||||
|
|
||||||
test('vim: no operations', (t) => {
|
test('vim: no operations', (t) => {
|
||||||
const result = vim('hello', {});
|
const result = vim('hello', {});
|
||||||
|
|
@ -10,3 +8,93 @@ test('vim: no operations', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('vim: space', (t) => {
|
||||||
|
const moveNext = stub();
|
||||||
|
|
||||||
|
vim(' ');
|
||||||
|
vim('j', {
|
||||||
|
moveNext,
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = [{
|
||||||
|
count: 1,
|
||||||
|
isDelete: false,
|
||||||
|
isVisual: false,
|
||||||
|
}];
|
||||||
|
|
||||||
|
t.calledWith(moveNext, args);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vim: ^', (t) => {
|
||||||
|
const movePrevious = stub();
|
||||||
|
|
||||||
|
vim('^', {
|
||||||
|
movePrevious,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
count: Infinity,
|
||||||
|
isVisual: false,
|
||||||
|
isDelete: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(movePrevious, [expected], 'should call movePrevious');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vim: cc', (t) => {
|
||||||
|
const operationCopy = stub();
|
||||||
|
|
||||||
|
vim('cc', {
|
||||||
|
operationCopy,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(operationCopy);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vim: mm', (t) => {
|
||||||
|
const operationMove = stub();
|
||||||
|
|
||||||
|
vim('mm', {
|
||||||
|
operationMove,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(operationMove);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vim: w', (t) => {
|
||||||
|
const moveNext = stub();
|
||||||
|
|
||||||
|
vim('w', {
|
||||||
|
moveNext,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
count: 1,
|
||||||
|
isVisual: false,
|
||||||
|
isDelete: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(moveNext, [expected], 'should call moveNext');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vim: b', (t) => {
|
||||||
|
const movePrevious = stub();
|
||||||
|
|
||||||
|
vim('b', {
|
||||||
|
movePrevious,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
count: 1,
|
||||||
|
isVisual: false,
|
||||||
|
isDelete: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
t.calledWith(movePrevious, [expected], 'should call movePrevious');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
'use strict';
|
import currify from 'currify';
|
||||||
|
|
||||||
module.exports = (array, item) => {
|
export const getIndex = currify((array, item) => {
|
||||||
const index = array.indexOf(item);
|
const index = array.indexOf(item);
|
||||||
|
|
||||||
if (!~index)
|
if (!~index)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
};
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
'use strict';
|
import test from 'supertape';
|
||||||
|
import {getIndex} from './get-index.js';
|
||||||
const test = require('supertape');
|
|
||||||
|
|
||||||
const dir = '../../../client/listeners';
|
|
||||||
const getIndex = require(`${dir}/get-index`);
|
|
||||||
|
|
||||||
test('cloudcmd: client: listeners: getIndex: not found', (t) => {
|
test('cloudcmd: client: listeners: getIndex: not found', (t) => {
|
||||||
const array = ['hello'];
|
const array = ['hello'];
|
||||||
|
|
@ -13,9 +9,11 @@ test('cloudcmd: client: listeners: getIndex: not found', (t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: listeners: getIndex: found', (t) => {
|
test('cloudcmd: client: listeners: getIndex: found', (t) => {
|
||||||
const array = ['hello', 'world'];
|
const array = [
|
||||||
|
'hello',
|
||||||
|
'world',
|
||||||
|
];
|
||||||
|
|
||||||
t.equal(getIndex(array, 'world'), 1, 'should return index');
|
t.equal(getIndex(array, 'world'), 1, 'should return index');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
'use strict';
|
export const getRange = (indexFrom, indexTo, files) => {
|
||||||
|
|
||||||
module.exports = (indexFrom, indexTo, files) => {
|
|
||||||
if (indexFrom < indexTo)
|
if (indexFrom < indexTo)
|
||||||
return files.slice(indexFrom, indexTo + 1);
|
return files.slice(indexFrom, indexTo + 1);
|
||||||
|
|
||||||
|
|
@ -9,4 +7,3 @@ module.exports = (indexFrom, indexTo, files) => {
|
||||||
|
|
||||||
return [files[indexFrom]];
|
return [files[indexFrom]];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
52
client/listeners/get-range.spec.js
Normal file
52
client/listeners/get-range.spec.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import test from 'supertape';
|
||||||
|
import {getRange} from './get-range.js';
|
||||||
|
|
||||||
|
test('cloudcmd: client: listeners: getRange: direct', (t) => {
|
||||||
|
const expected = [
|
||||||
|
'hello',
|
||||||
|
'world',
|
||||||
|
];
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
...expected,
|
||||||
|
'how',
|
||||||
|
'come',
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = getRange(0, 1, files);
|
||||||
|
|
||||||
|
t.deepEqual(result, expected, 'should return range');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: listeners: getRange: reverse', (t) => {
|
||||||
|
const expected = [
|
||||||
|
'hello',
|
||||||
|
'world',
|
||||||
|
];
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
...expected,
|
||||||
|
'how',
|
||||||
|
'come',
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = getRange(1, 0, files);
|
||||||
|
|
||||||
|
t.deepEqual(result, expected, 'should return range');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: listeners: getRange: one', (t) => {
|
||||||
|
const expected = ['hello'];
|
||||||
|
const files = [
|
||||||
|
...expected,
|
||||||
|
'how',
|
||||||
|
'come',
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = getRange(0, 0, files);
|
||||||
|
|
||||||
|
t.deepEqual(result, expected, 'should return range');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,36 +1,30 @@
|
||||||
/* global DOM, CloudCmd */
|
/* global DOM, CloudCmd */
|
||||||
|
import exec from 'execon';
|
||||||
'use strict';
|
import itype from 'itype';
|
||||||
|
import currify from 'currify';
|
||||||
const exec = require('execon');
|
import {tryToCatch} from 'try-to-catch';
|
||||||
const itype = require('itype');
|
import clipboard from '@cloudcmd/clipboard';
|
||||||
const currify = require('currify');
|
import * as Events from '#dom/events';
|
||||||
const tryToCatch = require('try-to-catch');
|
import {uploadFiles} from '#dom/upload-files';
|
||||||
const clipboard = require('@cloudcmd/clipboard');
|
import {FS} from '#common/cloudfunc';
|
||||||
|
import {getRange} from './get-range.js';
|
||||||
const getRange = require('./get-range');
|
import {getIndex} from './get-index.js';
|
||||||
const getIndex = currify(require('./get-index'));
|
|
||||||
const uploadFiles = require('../dom/upload-files');
|
|
||||||
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
|
|
||||||
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
||||||
const SPACE = ' ';
|
const SPACE = ' ';
|
||||||
|
|
||||||
module.exports.init = () => {
|
export async function init() {
|
||||||
contextMenu();
|
contextMenu();
|
||||||
dragndrop();
|
dragndrop();
|
||||||
unload();
|
unload();
|
||||||
pop();
|
pop();
|
||||||
resize();
|
resize();
|
||||||
config();
|
|
||||||
header();
|
header();
|
||||||
};
|
await config();
|
||||||
|
}
|
||||||
CloudCmd.Listeners = module.exports;
|
|
||||||
|
|
||||||
const unselect = (event) => {
|
const unselect = (event) => {
|
||||||
const isMac = /Mac/.test(window.navigator.platform);
|
const isMac = /Mac/.test(globalThis.navigator.platform);
|
||||||
const {
|
const {
|
||||||
shiftKey,
|
shiftKey,
|
||||||
metaKey,
|
metaKey,
|
||||||
|
|
@ -48,14 +42,9 @@ const execAll = currify((funcs, event) => {
|
||||||
fn(event);
|
fn(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const {Events} = DOM;
|
|
||||||
const EventsFiles = {
|
const EventsFiles = {
|
||||||
mousedown: exec.with(execIfNotUL, setCurrentFileByEvent),
|
mousedown: exec.with(execIfNotUL, setCurrentFileByEvent),
|
||||||
click: execAll([
|
click: execAll([onClick, exec.with(execIfNotMobile, unselect)]),
|
||||||
onClick,
|
|
||||||
unselect,
|
|
||||||
]),
|
|
||||||
dragstart: exec.with(execIfNotUL, onDragStart),
|
dragstart: exec.with(execIfNotUL, onDragStart),
|
||||||
dblclick: exec.with(execIfNotUL, onDblClick),
|
dblclick: exec.with(execIfNotUL, onDblClick),
|
||||||
touchstart: exec.with(execIfNotUL, onTouch),
|
touchstart: exec.with(execIfNotUL, onTouch),
|
||||||
|
|
@ -66,6 +55,7 @@ let EXT;
|
||||||
function header() {
|
function header() {
|
||||||
const fm = DOM.getFM();
|
const fm = DOM.getFM();
|
||||||
const isDataset = (el) => el.dataset;
|
const isDataset = (el) => el.dataset;
|
||||||
|
|
||||||
const isPanel = (el) => {
|
const isPanel = (el) => {
|
||||||
return /^js-(left|right)$/.test(el.dataset.name);
|
return /^js-(left|right)$/.test(el.dataset.name);
|
||||||
};
|
};
|
||||||
|
|
@ -77,8 +67,7 @@ function header() {
|
||||||
if (parent.dataset.name !== 'js-fm-header')
|
if (parent.dataset.name !== 'js-fm-header')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const name = (el.dataset.name || '')
|
const name = (el.dataset.name || '').replace('js-', '');
|
||||||
.replace('js-', '');
|
|
||||||
|
|
||||||
if (!/^(name|size|date)$/.test(name))
|
if (!/^(name|size|date)$/.test(name))
|
||||||
return;
|
return;
|
||||||
|
|
@ -101,36 +90,40 @@ function getPath(el, path = []) {
|
||||||
|
|
||||||
async function config() {
|
async function config() {
|
||||||
const [, config] = await tryToCatch(DOM.Files.get, 'config');
|
const [, config] = await tryToCatch(DOM.Files.get, 'config');
|
||||||
const type = config && config.packer;
|
const type = config?.packer;
|
||||||
|
|
||||||
EXT = DOM.getPackerExt(type);
|
EXT = DOM.getPackerExt(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.initKeysPanel = () => {
|
export const initKeysPanel = () => {
|
||||||
const keysElement = DOM.getById('js-keyspanel');
|
const keysElement = DOM.getById('js-keyspanel');
|
||||||
|
|
||||||
if (!keysElement)
|
if (!keysElement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Events.addClick(keysElement, ({target}) => {
|
Events.addClick(keysElement, (event) => {
|
||||||
|
const {target} = event;
|
||||||
const {id} = target;
|
const {id} = target;
|
||||||
|
|
||||||
const operation = (name) => {
|
const operation = (name) => {
|
||||||
const {Operation} = CloudCmd;
|
const {Operation} = CloudCmd;
|
||||||
const fn = Operation.show.bind(null, name);
|
|
||||||
|
|
||||||
return fn;
|
return Operation.show.bind(null, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickFuncs = {
|
const clickFuncs = {
|
||||||
'f1': CloudCmd.Help.show,
|
'f1': CloudCmd.Help.show,
|
||||||
'f2' : initF2,
|
'f2': CloudCmd.UserMenu.show,
|
||||||
'f3': CloudCmd.View.show,
|
'f3': CloudCmd.View.show,
|
||||||
'f4': CloudCmd.EditFile.show,
|
'f4': CloudCmd.EditFile.show,
|
||||||
'f5': operation('copy'),
|
'f5': operation('copy'),
|
||||||
'f6': operation('move'),
|
'f6': operation('move'),
|
||||||
'f7': DOM.promptNewDir,
|
'f7': DOM.promptNewDir,
|
||||||
'f8': operation('delete'),
|
'f8': operation('delete'),
|
||||||
'f9' : CloudCmd.Menu.show,
|
'f9': () => {
|
||||||
|
event.stopPropagation();
|
||||||
|
CloudCmd.Menu.show();
|
||||||
|
},
|
||||||
'f10': CloudCmd.Config.show,
|
'f10': CloudCmd.Config.show,
|
||||||
'~': CloudCmd.Konsole.show,
|
'~': CloudCmd.Konsole.show,
|
||||||
'shift~': CloudCmd.Terminal.show,
|
'shift~': CloudCmd.Terminal.show,
|
||||||
|
|
@ -141,18 +134,14 @@ module.exports.initKeysPanel = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function initF2() {
|
|
||||||
CloudCmd.UserMenu.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPanel = (side) => {
|
const getPanel = (side) => {
|
||||||
if (!itype.string(side))
|
if (!itype.string(side))
|
||||||
return side;
|
return side;
|
||||||
|
|
||||||
return DOM.getByDataName('js-' + side);
|
return DOM.getByDataName(`js-${side}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setOnPanel = (side) => {
|
export const setOnPanel = (side) => {
|
||||||
const panel = getPanel(side);
|
const panel = getPanel(side);
|
||||||
|
|
||||||
const filesElement = DOM.getByDataName('js-files', panel);
|
const filesElement = DOM.getByDataName('js-files', panel);
|
||||||
|
|
@ -168,6 +157,7 @@ function getPathListener(panel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNoCurrent(panel) {
|
function isNoCurrent(panel) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const infoPanel = Info.panel;
|
const infoPanel = Info.panel;
|
||||||
|
|
||||||
if (!infoPanel)
|
if (!infoPanel)
|
||||||
|
|
@ -186,13 +176,13 @@ function decodePath(path) {
|
||||||
|
|
||||||
return decodeURI(path)
|
return decodeURI(path)
|
||||||
.replace(url, '')
|
.replace(url, '')
|
||||||
.replace(prefixReg, '')
|
.replace(prefixReg, '') // browser doesn't replace % -> %25% do it for him
|
||||||
// browser doesn't replace % -> %25% do it for him
|
|
||||||
.replace('%%', '%25%')
|
.replace('%%', '%25%')
|
||||||
.replace(NBSP_REG, SPACE) || '/';
|
.replace(NBSP_REG, SPACE) || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPathElementClick(panel, event) {
|
async function onPathElementClick(panel, event) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const element = event.target;
|
const element = event.target;
|
||||||
|
|
@ -214,19 +204,26 @@ async function onPathElementClick(panel, event) {
|
||||||
const {href} = element;
|
const {href} = element;
|
||||||
const path = decodePath(href);
|
const path = decodePath(href);
|
||||||
|
|
||||||
await CloudCmd.loadDir({
|
await CloudCmd.changeDir(path, {
|
||||||
path,
|
|
||||||
isRefresh: false,
|
isRefresh: false,
|
||||||
panel: noCurrent ? panel : Info.panel,
|
panel: noCurrent ? panel : Info.panel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyPath(el) {
|
function copyPath(el) {
|
||||||
clipboard.writeText(el.parentElement.title)
|
clipboard
|
||||||
|
.writeText(el.parentElement.title)
|
||||||
.then(CloudCmd.log)
|
.then(CloudCmd.log)
|
||||||
.catch(CloudCmd.log);
|
.catch(CloudCmd.log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function execIfNotMobile(callback, event) {
|
||||||
|
const isMobile = DOM.getCSSVar('is-mobile');
|
||||||
|
|
||||||
|
if (!isMobile)
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
|
||||||
function execIfNotUL(callback, event) {
|
function execIfNotUL(callback, event) {
|
||||||
const {target} = event;
|
const {target} = event;
|
||||||
const {tagName} = target;
|
const {tagName} = target;
|
||||||
|
|
@ -241,14 +238,14 @@ function onClick(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelect(key, files) {
|
function toggleSelect(key, files) {
|
||||||
const isMac = /Mac/.test(window.navigator.platform);
|
const isMac = /Mac/.test(globalThis.navigator.platform);
|
||||||
|
|
||||||
if (!key)
|
if (!key)
|
||||||
throw Error('key should not be undefined!');
|
throw Error('key should not be undefined!');
|
||||||
|
|
||||||
const [file] = files;
|
const [file] = files;
|
||||||
|
|
||||||
if (isMac && key.meta || key.ctrl)
|
if (isMac && key.meta)
|
||||||
return DOM.toggleSelectedFile(file);
|
return DOM.toggleSelectedFile(file);
|
||||||
|
|
||||||
if (key.shift)
|
if (key.shift)
|
||||||
|
|
@ -256,6 +253,7 @@ function toggleSelect(key, files) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function changePanel(element) {
|
function changePanel(element) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const {panel} = Info;
|
const {panel} = Info;
|
||||||
const files = DOM.getByDataName('js-files', panel);
|
const files = DOM.getByDataName('js-files', panel);
|
||||||
const ul = getULElement(element);
|
const ul = getULElement(element);
|
||||||
|
|
@ -265,21 +263,16 @@ function changePanel(element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onDblClick(event) {
|
async function onDblClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
const current = getLIElement(event.target);
|
const current = getLIElement(event.target);
|
||||||
const isDir = DOM.isCurrentIsDir(current);
|
const isDir = DOM.isCurrentIsDir(current);
|
||||||
const path = DOM.getCurrentPath(current);
|
const path = DOM.getCurrentPath(current);
|
||||||
|
|
||||||
if (isDir) {
|
if (!isDir)
|
||||||
await CloudCmd.loadDir({
|
return CloudCmd.View.show();
|
||||||
path: path === '/' ? '/' : path + '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
event.preventDefault();
|
await CloudCmd.changeDir(path);
|
||||||
} else {
|
|
||||||
CloudCmd.View.show();
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTouch(event) {
|
async function onTouch(event) {
|
||||||
|
|
@ -294,9 +287,7 @@ async function onTouch(event) {
|
||||||
if (!isCurrent)
|
if (!isCurrent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await CloudCmd.loadDir({
|
await CloudCmd.changeDir(DOM.getCurrentPath(current));
|
||||||
path: DOM.getCurrentPath(current),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -304,6 +295,7 @@ async function onTouch(event) {
|
||||||
* in Chrome (HTML5)
|
* in Chrome (HTML5)
|
||||||
*/
|
*/
|
||||||
function onDragStart(event) {
|
function onDragStart(event) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const {prefixURL} = CloudCmd;
|
const {prefixURL} = CloudCmd;
|
||||||
const element = getLIElement(event.target);
|
const element = getLIElement(event.target);
|
||||||
const {isDir} = Info;
|
const {isDir} = Info;
|
||||||
|
|
@ -320,8 +312,8 @@ function onDragStart(event) {
|
||||||
|
|
||||||
event.dataTransfer.setData(
|
event.dataTransfer.setData(
|
||||||
'DownloadURL',
|
'DownloadURL',
|
||||||
'application/octet-stream' + ':' +
|
'application/octet-stream' + ':' + name +
|
||||||
name + ':' +
|
':' +
|
||||||
link,
|
link,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -344,6 +336,7 @@ function getULElement(element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCurrentFileByEvent(event) {
|
function setCurrentFileByEvent(event) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const BUTTON_LEFT = 0;
|
const BUTTON_LEFT = 0;
|
||||||
|
|
||||||
const key = {
|
const key = {
|
||||||
|
|
@ -407,10 +400,7 @@ function dragndrop() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDrop = (event) => {
|
const onDrop = (event) => {
|
||||||
const {
|
const {files, items} = event.dataTransfer;
|
||||||
files,
|
|
||||||
items,
|
|
||||||
} = event.dataTransfer;
|
|
||||||
|
|
||||||
const {length: filesCount} = files;
|
const {length: filesCount} = files;
|
||||||
|
|
||||||
|
|
@ -420,7 +410,10 @@ function dragndrop() {
|
||||||
return uploadFiles(files);
|
return uploadFiles(files);
|
||||||
|
|
||||||
const isFile = (item) => item.kind === 'file';
|
const isFile = (item) => item.kind === 'file';
|
||||||
const dirFiles = Array.from(items).filter(isFile);
|
|
||||||
|
const dirFiles = Array
|
||||||
|
.from(items)
|
||||||
|
.filter(isFile);
|
||||||
|
|
||||||
if (dirFiles.length)
|
if (dirFiles.length)
|
||||||
return DOM.uploadDirectory(dirFiles);
|
return DOM.uploadDirectory(dirFiles);
|
||||||
|
|
@ -432,7 +425,7 @@ function dragndrop() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In Mac OS Chrome dropEffect = 'none'
|
* In macOS Chrome dropEffect = 'none'
|
||||||
* so drop do not firing up when try
|
* so drop do not firing up when try
|
||||||
* to upload file from download bar
|
* to upload file from download bar
|
||||||
*/
|
*/
|
||||||
|
|
@ -457,9 +450,9 @@ function dragndrop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function unload() {
|
function unload() {
|
||||||
DOM.Events.add(['unload', 'beforeunload'], (event) => {
|
Events.add(['unload', 'beforeunload'], (event) => {
|
||||||
const {Key} = CloudCmd;
|
const {Key} = CloudCmd;
|
||||||
const isBind = Key && Key.isBind();
|
const isBind = Key?.isBind();
|
||||||
|
|
||||||
if (isBind)
|
if (isBind)
|
||||||
return;
|
return;
|
||||||
|
|
@ -477,8 +470,8 @@ function pop() {
|
||||||
return CloudCmd.route(location.hash);
|
return CloudCmd.route(location.hash);
|
||||||
|
|
||||||
const history = false;
|
const history = false;
|
||||||
await CloudCmd.loadDir({
|
|
||||||
path,
|
await CloudCmd.changeDir(path, {
|
||||||
history,
|
history,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -486,7 +479,8 @@ function pop() {
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
Events.add('resize', () => {
|
Events.add('resize', () => {
|
||||||
const is = window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
|
const Info = DOM.CurrentInfo;
|
||||||
|
const is = globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
|
||||||
|
|
||||||
if (!is)
|
if (!is)
|
||||||
return;
|
return;
|
||||||
|
|
@ -506,4 +500,3 @@ function resize() {
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
|
import exec from 'execon';
|
||||||
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import {js as loadJS} from 'load.js';
|
||||||
|
import pascalCase from 'just-pascal-case';
|
||||||
|
|
||||||
const exec = require('execon');
|
|
||||||
const tryToCatch = require('try-to-catch');
|
|
||||||
const loadJS = require('load.js').js;
|
|
||||||
|
|
||||||
const pascalCase = require('just-pascal-case');
|
|
||||||
const noJS = (a) => a.replace(/.js$/, '');
|
const noJS = (a) => a.replace(/.js$/, '');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* function load modules
|
* function load modules
|
||||||
* @params = {name, path, func, dobefore, arg}
|
* @params = {name, path, func, dobefore, arg}
|
||||||
*/
|
*/
|
||||||
module.exports = function loadModule(params) {
|
export const loadModule = (params) => {
|
||||||
if (!params)
|
if (!params)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -25,24 +22,24 @@ module.exports = function loadModule(params) {
|
||||||
if (CloudCmd[name])
|
if (CloudCmd[name])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CloudCmd[name] = () => {
|
CloudCmd[name] = async () => {
|
||||||
exec(doBefore);
|
exec(doBefore);
|
||||||
const {prefix} = CloudCmd;
|
|
||||||
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path + '.js';
|
|
||||||
|
|
||||||
return loadJS(pathFull).then(async () => {
|
const {DIR_MODULES} = CloudCmd;
|
||||||
|
const pathFull = `${DIR_MODULES}/${path}.js`;
|
||||||
|
|
||||||
|
await loadJS(pathFull);
|
||||||
const newModule = async (f) => f && f();
|
const newModule = async (f) => f && f();
|
||||||
const module = CloudCmd[name];
|
const module = CloudCmd[name];
|
||||||
|
|
||||||
Object.assign(newModule, module);
|
Object.assign(newModule, module);
|
||||||
|
|
||||||
CloudCmd[name] = newModule;
|
CloudCmd[name] = newModule;
|
||||||
|
|
||||||
CloudCmd.log('init', name);
|
CloudCmd.log('init', name);
|
||||||
|
|
||||||
await module.init();
|
await module.init();
|
||||||
|
|
||||||
return newModule;
|
return newModule;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CloudCmd[name].show = async (...args) => {
|
CloudCmd[name].show = async (...args) => {
|
||||||
|
|
@ -52,9 +49,8 @@ module.exports = function loadModule(params) {
|
||||||
const [e, a] = await tryToCatch(m);
|
const [e, a] = await tryToCatch(m);
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
return console.error(e);
|
return;
|
||||||
|
|
||||||
await a.show(...args);
|
return await a.show(...args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
/* global CloudCmd, filepicker */
|
/* global CloudCmd, filepicker */
|
||||||
|
import exec from 'execon';
|
||||||
'use strict';
|
import currify from 'currify';
|
||||||
|
import load from 'load.js';
|
||||||
const exec = require('execon');
|
import {ajax} from '#dom/load';
|
||||||
const currify = require('currify');
|
import * as Files from '#dom/files';
|
||||||
const load = require('load.js');
|
import * as Images from '#dom/images';
|
||||||
|
|
||||||
const {log} = CloudCmd;
|
const {log} = CloudCmd;
|
||||||
|
|
||||||
const {ajax} = require('../dom/load');
|
|
||||||
const Files = require('../dom/files');
|
|
||||||
const Images = require('../dom/images');
|
|
||||||
|
|
||||||
const upload = currify(_upload);
|
const upload = currify(_upload);
|
||||||
|
|
||||||
const Name = 'Cloud';
|
const Name = 'Cloud';
|
||||||
CloudCmd[Name] = module.exports;
|
|
||||||
|
|
||||||
module.exports.init = async () => {
|
CloudCmd[Name] = {
|
||||||
|
init,
|
||||||
|
uploadFile,
|
||||||
|
saveFile,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function init() {
|
||||||
const [modules] = await loadFiles();
|
const [modules] = await loadFiles();
|
||||||
const {key} = modules.data.FilePicker;
|
const {key} = modules.data.FilePicker;
|
||||||
|
|
||||||
filepicker.setKey(key);
|
filepicker.setKey(key);
|
||||||
Images.hide();
|
Images.hide();
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.uploadFile = (filename, data) => {
|
export function uploadFile(filename, data) {
|
||||||
const mimetype = '';
|
const mimetype = '';
|
||||||
|
|
||||||
filepicker.store(data, {
|
filepicker.store(data, {
|
||||||
|
|
@ -34,17 +35,14 @@ module.exports.uploadFile = (filename, data) => {
|
||||||
}, (fpFile) => {
|
}, (fpFile) => {
|
||||||
filepicker.exportFile(fpFile, log, log);
|
filepicker.exportFile(fpFile, log, log);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.saveFile = (callback) => {
|
export function saveFile(callback) {
|
||||||
filepicker.pick(upload(callback));
|
filepicker.pick(upload(callback));
|
||||||
};
|
}
|
||||||
|
|
||||||
function _upload(callback, file) {
|
function _upload(callback, file) {
|
||||||
const {
|
const {url, filename} = file;
|
||||||
url,
|
|
||||||
filename,
|
|
||||||
} = file;
|
|
||||||
|
|
||||||
const responseType = 'arraybuffer';
|
const responseType = 'arraybuffer';
|
||||||
const success = exec.with(callback, filename);
|
const success = exec.with(callback, filename);
|
||||||
|
|
@ -56,7 +54,7 @@ function _upload(callback, file) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFiles() {
|
function loadFiles() {
|
||||||
const js = '//api.filepicker.io/v2/filepicker.js';
|
const js = '//api.filepicker.io/v2/filepicker.js';
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
|
@ -64,4 +62,3 @@ async function loadFiles() {
|
||||||
load.js(js),
|
load.js(js),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
31
client/modules/command-line.js
Normal file
31
client/modules/command-line.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* global CloudCmd */
|
||||||
|
import * as Dialog from '#dom/dialog';
|
||||||
|
|
||||||
|
export function init() {}
|
||||||
|
CloudCmd.CommandLine = {
|
||||||
|
init,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function show() {
|
||||||
|
const [, cmd] = await Dialog.prompt('Command Line', '');
|
||||||
|
const TERMINAL = '^(t|terminal)';
|
||||||
|
|
||||||
|
if (RegExp(`${TERMINAL}$`).test(cmd))
|
||||||
|
return await CloudCmd.Terminal.show();
|
||||||
|
|
||||||
|
if (RegExp(TERMINAL).test(cmd)) {
|
||||||
|
const command = cmd.replace(RegExp(`${TERMINAL} `), '');
|
||||||
|
const exitCode = await CloudCmd.TerminalRun.show({
|
||||||
|
command: `bash -c '${command}'`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exitCode === -1)
|
||||||
|
await Dialog.alert(`☝️ Looks like Terminal is disabled, start Cloud Coammnder with '--terminal' flag.`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hide() {}
|
||||||
|
|
@ -1,28 +1,23 @@
|
||||||
'use strict';
|
import '../../../css/config.css';
|
||||||
|
import {rendy} from 'rendy';
|
||||||
|
import currify from 'currify';
|
||||||
|
import wraptile from 'wraptile';
|
||||||
|
import squad from 'squad';
|
||||||
|
import {promisify} from 'es6-promisify';
|
||||||
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import load from 'load.js';
|
||||||
|
import createElement from '@cloudcmd/create-element';
|
||||||
|
import * as Events from '#dom/events';
|
||||||
|
import * as Files from '#dom/files';
|
||||||
|
import {getTitle} from '#common/cloudfunc';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
import * as input from './input.js';
|
||||||
|
|
||||||
/* global CloudCmd, DOM, io */
|
const {CloudCmd, DOM} = globalThis;
|
||||||
|
|
||||||
require('../../../css/config.css');
|
|
||||||
|
|
||||||
const rendy = require('rendy');
|
|
||||||
const currify = require('currify');
|
|
||||||
const wraptile = require('wraptile');
|
|
||||||
const squad = require('squad');
|
|
||||||
const {promisify} = require('es6-promisify');
|
|
||||||
const tryToCatch = require('try-to-catch');
|
|
||||||
const load = require('load.js');
|
|
||||||
const createElement = require('@cloudcmd/create-element');
|
|
||||||
|
|
||||||
const input = require('./input');
|
|
||||||
const Images = require('../../dom/images');
|
|
||||||
const Events = require('../../dom/events');
|
|
||||||
const Files = require('../../dom/files');
|
|
||||||
|
|
||||||
const {getTitle} = require('../../../common/cloudfunc');
|
|
||||||
const {Dialog, setTitle} = DOM;
|
const {Dialog, setTitle} = DOM;
|
||||||
|
|
||||||
const Name = 'Config';
|
const Name = 'Config';
|
||||||
CloudCmd[Name] = module.exports;
|
|
||||||
|
|
||||||
const loadSocket = promisify(DOM.loadSocket);
|
const loadSocket = promisify(DOM.loadSocket);
|
||||||
|
|
||||||
|
|
@ -46,28 +41,25 @@ let Template;
|
||||||
|
|
||||||
const loadCSS = load.css;
|
const loadCSS = load.css;
|
||||||
|
|
||||||
module.exports.init = async () => {
|
export async function init() {
|
||||||
if (!CloudCmd.config('configDialog'))
|
if (!CloudCmd.config('configDialog'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showLoad();
|
showLoad();
|
||||||
|
|
||||||
const {prefix} = CloudCmd;
|
const {DIR_DIST} = CloudCmd;
|
||||||
|
|
||||||
[Template] = await Promise.all([
|
[Template] = await Promise.all([
|
||||||
Files.get('config-tmpl'),
|
Files.get('config-tmpl'),
|
||||||
loadSocket(),
|
loadSocket(),
|
||||||
loadCSS(prefix + '/dist/config.css'),
|
loadCSS(`${DIR_DIST}/config.css`),
|
||||||
CloudCmd.View(),
|
CloudCmd.View(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
initSocket();
|
initSocket();
|
||||||
};
|
}
|
||||||
|
|
||||||
const {
|
const {config, Key} = CloudCmd;
|
||||||
config,
|
|
||||||
Key,
|
|
||||||
} = CloudCmd;
|
|
||||||
|
|
||||||
let Element;
|
let Element;
|
||||||
|
|
||||||
|
|
@ -77,24 +69,20 @@ function getHost() {
|
||||||
origin,
|
origin,
|
||||||
protocol,
|
protocol,
|
||||||
} = location;
|
} = location;
|
||||||
const href = origin || `${protocol}//${host}`;
|
|
||||||
|
|
||||||
return href;
|
return origin || `${protocol}//${host}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSocket() {
|
function initSocket() {
|
||||||
const href = getHost();
|
const href = getHost();
|
||||||
const {
|
const {prefixSocket, prefix} = CloudCmd;
|
||||||
prefixSocket,
|
|
||||||
prefix,
|
|
||||||
} = CloudCmd;
|
|
||||||
|
|
||||||
const ONE_MINUTE = 60 * 1000;
|
const ONE_MINUTE = 60 * 1000;
|
||||||
|
|
||||||
const socket = io.connect(href + prefixSocket + '/config', {
|
const socket = globalThis.io.connect(href + prefixSocket + '/config', {
|
||||||
reconnectionAttempts: Infinity,
|
reconnectionAttempts: Infinity,
|
||||||
reconnectionDelay: ONE_MINUTE,
|
reconnectionDelay: ONE_MINUTE,
|
||||||
path: prefix + '/socket.io',
|
path: `${prefix}/socket.io`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = (data) => {
|
const save = (data) => {
|
||||||
|
|
@ -125,9 +113,7 @@ function authCheck(socket) {
|
||||||
|
|
||||||
Config.save = saveHttp;
|
Config.save = saveHttp;
|
||||||
|
|
||||||
module.exports.show = show;
|
export async function show() {
|
||||||
|
|
||||||
async function show() {
|
|
||||||
if (!CloudCmd.config('configDialog'))
|
if (!CloudCmd.config('configDialog'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -142,16 +128,22 @@ async function fillTemplate() {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
editor,
|
editor,
|
||||||
|
menu,
|
||||||
packer,
|
packer,
|
||||||
columns,
|
columns,
|
||||||
|
theme,
|
||||||
configAuth,
|
configAuth,
|
||||||
|
configPort,
|
||||||
...obj
|
...obj
|
||||||
} = input.convert(config);
|
} = input.convert(config);
|
||||||
|
|
||||||
obj[editor + '-selected'] = 'selected';
|
obj[`${menu}-selected`] = 'selected';
|
||||||
obj[packer + '-selected'] = 'selected';
|
obj[`${editor}-selected`] = 'selected';
|
||||||
obj[columns + '-selected'] = 'selected';
|
obj[`${packer}-selected`] = 'selected';
|
||||||
|
obj[`${columns}-selected`] = 'selected';
|
||||||
|
obj[`${theme}-selected`] = 'selected';
|
||||||
obj.configAuth = configAuth ? '' : 'hidden';
|
obj.configAuth = configAuth ? '' : 'hidden';
|
||||||
|
obj.configPort = configPort ? '' : 'hidden';
|
||||||
|
|
||||||
const innerHTML = rendy(Template, obj);
|
const innerHTML = rendy(Template, obj);
|
||||||
|
|
||||||
|
|
@ -173,20 +165,20 @@ async function fillTemplate() {
|
||||||
const getTarget = ({target}) => target;
|
const getTarget = ({target}) => target;
|
||||||
const handleChange = squad(onChange, getTarget);
|
const handleChange = squad(onChange, getTarget);
|
||||||
|
|
||||||
Array.from(inputs)
|
Array
|
||||||
|
.from(inputs)
|
||||||
.map(addKey(onKey))
|
.map(addKey(onKey))
|
||||||
.map(addChange(handleChange));
|
.map(addChange(handleChange));
|
||||||
|
|
||||||
const autoSize = true;
|
const autoSize = true;
|
||||||
|
|
||||||
CloudCmd.View.show(Element, {
|
CloudCmd.View.show(Element, {
|
||||||
autoSize,
|
autoSize,
|
||||||
afterShow,
|
afterShow,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.hide = hide;
|
export function hide() {
|
||||||
|
|
||||||
function hide() {
|
|
||||||
CloudCmd.View.hide();
|
CloudCmd.View.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +220,7 @@ function onAuthChange(checked) {
|
||||||
const elUsername = input.getElementByName('username', Element);
|
const elUsername = input.getElementByName('username', Element);
|
||||||
const elPassword = input.getElementByName('password', Element);
|
const elPassword = input.getElementByName('password', Element);
|
||||||
|
|
||||||
elUsername.disabled =
|
elUsername.disabled = !checked;
|
||||||
elPassword.disabled = !checked;
|
elPassword.disabled = !checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,13 +230,18 @@ function onNameChange(name) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKey({keyCode, target}) {
|
async function onKey({keyCode, target}) {
|
||||||
switch(keyCode) {
|
switch(keyCode) {
|
||||||
case Key.ESC:
|
case Key.ESC:
|
||||||
return hide();
|
return hide();
|
||||||
|
|
||||||
case Key.ENTER:
|
case Key.ENTER:
|
||||||
return onChange(target);
|
return await onChange(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloudCmd[Name] = {
|
||||||
|
init,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,17 @@
|
||||||
'use strict';
|
import {encode} from '#common/entity';
|
||||||
|
|
||||||
const currify = require('currify');
|
const isBool = (a) => typeof a === 'boolean';
|
||||||
|
const isString = (a) => typeof a === 'string';
|
||||||
|
|
||||||
const isType = currify((type, object, name) => {
|
const {keys} = Object;
|
||||||
return typeof object[name] === type;
|
|
||||||
});
|
|
||||||
|
|
||||||
const isBool = isType('boolean');
|
export function getElementByName(selector, element) {
|
||||||
|
|
||||||
module.exports.getElementByName = getElementByName;
|
|
||||||
|
|
||||||
function getElementByName(selector, element) {
|
|
||||||
const str = `[data-name="js-${selector}"]`;
|
const str = `[data-name="js-${selector}"]`;
|
||||||
|
|
||||||
return element
|
return element.querySelector(str);
|
||||||
.querySelector(str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getName = (element) => {
|
export const getName = (element) => {
|
||||||
const name = element
|
const name = element
|
||||||
.getAttribute('data-name')
|
.getAttribute('data-name')
|
||||||
.replace(/^js-/, '');
|
.replace(/^js-/, '');
|
||||||
|
|
@ -25,17 +19,21 @@ module.exports.getName = (element) => {
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.convert = (config) => {
|
export const convert = (config) => {
|
||||||
const result = {
|
const result = config;
|
||||||
...config,
|
|
||||||
};
|
|
||||||
const array = Object.keys(config);
|
|
||||||
|
|
||||||
const filtered = array.filter(isBool(config));
|
for (const name of keys(config)) {
|
||||||
|
|
||||||
for (const name of filtered) {
|
|
||||||
const item = config[name];
|
const item = config[name];
|
||||||
|
|
||||||
|
if (isBool(item)) {
|
||||||
result[name] = setState(item);
|
result[name] = setState(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isString(item)) {
|
||||||
|
result[name] = encode(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -48,7 +46,7 @@ function setState(state) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getValue = (name, element) => {
|
export const getValue = (name, element) => {
|
||||||
const el = getElementByName(name, element);
|
const el = getElementByName(name, element);
|
||||||
const {type} = el;
|
const {type} = el;
|
||||||
|
|
||||||
|
|
@ -64,7 +62,7 @@ module.exports.getValue = (name, element) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setValue = (name, value, element) => {
|
export const setValue = (name, value, element) => {
|
||||||
const el = getElementByName(name, element);
|
const el = getElementByName(name, element);
|
||||||
const {type} = el;
|
const {type} = el;
|
||||||
|
|
||||||
|
|
@ -78,4 +76,3 @@ module.exports.setValue = (name, value, element) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
140
client/modules/config/input.spec.js
Normal file
140
client/modules/config/input.spec.js
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import {
|
||||||
|
convert,
|
||||||
|
getName,
|
||||||
|
getValue,
|
||||||
|
setValue,
|
||||||
|
} from './input.js';
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: convert', (t) => {
|
||||||
|
const result = convert({
|
||||||
|
name: 'hello <world>',
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
name: 'hello <world>',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: convert: bool', (t) => {
|
||||||
|
const result = convert({
|
||||||
|
auth: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
auth: ' checked',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: convert: bool false', (t) => {
|
||||||
|
const result = convert({
|
||||||
|
auth: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
auth: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getName', (t) => {
|
||||||
|
const getAttribute = stub().returns('js-hello');
|
||||||
|
const element = {
|
||||||
|
getAttribute,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getName(element);
|
||||||
|
|
||||||
|
t.equal(result, 'hello', 'should strip js- prefix');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: checkbox', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('auth', element);
|
||||||
|
|
||||||
|
t.ok(result, 'should return checked value');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: number', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'number',
|
||||||
|
value: '42',
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('port', element);
|
||||||
|
|
||||||
|
t.equal(result, 42, 'should return number');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: default', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'text',
|
||||||
|
value: 'hello',
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('name', element);
|
||||||
|
|
||||||
|
t.equal(result, 'hello', 'should return value as is');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: setValue: checkbox', (t) => {
|
||||||
|
const el = {
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const querySelector = stub().returns(el);
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
setValue('auth', true, element);
|
||||||
|
|
||||||
|
t.ok(el.checked, 'should set checked');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: setValue: default', (t) => {
|
||||||
|
const el = {
|
||||||
|
type: 'text',
|
||||||
|
value: 'old',
|
||||||
|
};
|
||||||
|
|
||||||
|
const querySelector = stub().returns(el);
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
setValue('name', 'new', element);
|
||||||
|
|
||||||
|
t.equal(el.value, 'new', 'should set value');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,36 +1,34 @@
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
import olark from '@cloudcmd/olark';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
|
||||||
'use strict';
|
CloudCmd.Contact = {
|
||||||
|
init,
|
||||||
CloudCmd.Contact = exports;
|
show,
|
||||||
|
hide,
|
||||||
const olark = require('@cloudcmd/olark');
|
};
|
||||||
const Images = require('../dom/images');
|
|
||||||
|
|
||||||
const {Events} = DOM;
|
const {Events} = DOM;
|
||||||
const {Key} = CloudCmd;
|
const {Key} = CloudCmd;
|
||||||
|
|
||||||
module.exports.show = show;
|
export function init() {
|
||||||
module.exports.hide = hide;
|
|
||||||
|
|
||||||
module.exports.init = () => {
|
|
||||||
Events.addKey(onKey);
|
Events.addKey(onKey);
|
||||||
|
|
||||||
olark.identify('6216-545-10-4223');
|
olark.identify('6216-545-10-4223');
|
||||||
olark('api.box.onExpand', show);
|
olark('api.box.onExpand', show);
|
||||||
olark('api.box.onShow', show);
|
olark('api.box.onShow', show);
|
||||||
olark('api.box.onShrink', hide);
|
olark('api.box.onShrink', hide);
|
||||||
};
|
}
|
||||||
|
|
||||||
function show() {
|
export function show() {
|
||||||
Key.unsetBind();
|
Key.unsetBind();
|
||||||
Images.hide();
|
Images.hide();
|
||||||
|
|
||||||
olark('api.box.expand');
|
olark('api.box.expand');
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
export function hide() {
|
||||||
Key.setBind();
|
Key.setBind();
|
||||||
olark('api.box.hide');
|
olark('api.box.hide');
|
||||||
}
|
}
|
||||||
|
|
@ -39,4 +37,3 @@ function onKey({keyCode}) {
|
||||||
if (keyCode === Key.ESC)
|
if (keyCode === Key.ESC)
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue