mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-07-23 16:11:24 +00:00
Compare commits
2163 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 | ||
|
|
7d82ef92e9 | ||
|
|
8f9c6906a1 | ||
|
|
f506801337 | ||
|
|
e6c51f61ca | ||
|
|
75a1f9db17 | ||
|
|
e13ec9aaf7 | ||
|
|
5db9bc1606 | ||
|
|
ca06ebc233 | ||
|
|
57b5243509 | ||
|
|
5f95018abe | ||
|
|
2a8b8b57e1 | ||
|
|
13d0b149d1 | ||
|
|
d7a58ae791 | ||
|
|
67eef31520 | ||
|
|
82f3198bdd | ||
|
|
429045e419 | ||
|
|
a645b9317e | ||
|
|
a9e19d3b4b | ||
|
|
2267b3e6b2 | ||
|
|
5d589ee31d | ||
|
|
28caa05292 | ||
|
|
faef643956 | ||
|
|
dee9f02cf5 | ||
|
|
44a5235d30 | ||
|
|
00eecbf7c8 | ||
|
|
798d974677 | ||
|
|
545b5a5135 | ||
|
|
a8bd28318f | ||
|
|
67138dab0f | ||
|
|
2a170584e2 | ||
|
|
b596376051 | ||
|
|
24ab06c005 | ||
|
|
511e40fa72 | ||
|
|
754d4087b4 | ||
|
|
de9a26f8ff | ||
|
|
f69a6ea82b | ||
|
|
0098fb9975 | ||
|
|
b483481c81 | ||
|
|
5b9e250c95 | ||
|
|
2efba6c0d7 | ||
|
|
1614901631 | ||
|
|
96744e9857 | ||
|
|
2d08fb98b7 | ||
|
|
12e813c432 | ||
|
|
1ca0905c52 | ||
|
|
f9f5a948b8 | ||
|
|
31eff2fc92 | ||
|
|
04df87c992 | ||
|
|
ce503c8e9a | ||
|
|
0d3f777507 | ||
|
|
a5aa85acb1 | ||
|
|
8e2bea4230 | ||
|
|
4ce883c507 | ||
|
|
fe163ef5fb | ||
|
|
901fd252f1 | ||
|
|
ed58853acc | ||
|
|
defc0486a5 | ||
|
|
c463960bc1 | ||
|
|
575b9ab425 | ||
|
|
7444678eb3 | ||
|
|
87555ce7ba | ||
|
|
7648aee85f | ||
|
|
83dfba2eb6 | ||
|
|
6f570722f4 | ||
|
|
999412e2da | ||
|
|
d7f20c4b3b | ||
|
|
add3f456c7 | ||
|
|
88d5071400 | ||
|
|
a9ba166bd4 | ||
|
|
8a92a94a3e | ||
|
|
59bc7c4cd4 | ||
|
|
2f6cb2d2ad | ||
|
|
54f9e1271a | ||
|
|
2be3459b08 | ||
|
|
31d7609858 | ||
|
|
7fcc860bea | ||
|
|
a4c8736663 | ||
|
|
ba6dd8e50e | ||
|
|
8f819f1eea | ||
|
|
d5e3621a2a | ||
|
|
e5c33037e5 | ||
|
|
d0fdf1686d | ||
|
|
7eb31f9412 | ||
|
|
aeb6f76611 | ||
|
|
03cfca8a4b | ||
|
|
4aef2b6c38 | ||
|
|
395e4a1ee4 | ||
|
|
c1cf8b414f | ||
|
|
ba0179e62f | ||
|
|
075568d3ec | ||
|
|
132a837717 | ||
|
|
9d6784cebd | ||
|
|
a6e8a83366 | ||
|
|
dbf6167aba | ||
|
|
1abf7c5749 | ||
|
|
2d64b64d68 | ||
|
|
0ba7df4064 | ||
|
|
20e930dd81 | ||
|
|
d836c0a99f | ||
|
|
98734e345f | ||
|
|
314f200477 | ||
|
|
321d3ad306 | ||
|
|
2215cace30 | ||
|
|
56381ccd55 | ||
|
|
e86b5ce0cf | ||
|
|
8b6f93625b | ||
|
|
4be2a53509 | ||
|
|
4dffca0184 | ||
|
|
7fb2435f6b | ||
|
|
a62fef46a0 | ||
|
|
e3d141a619 | ||
|
|
037da11545 | ||
|
|
9778111c58 | ||
|
|
9311918278 | ||
|
|
c1a08d8ad3 | ||
|
|
4dd28a27d3 | ||
|
|
44d82a6c54 | ||
|
|
1ec55052e6 | ||
|
|
3560f5483e | ||
|
|
c44b1f66ee | ||
|
|
d0e2b83b54 | ||
|
|
9e284e1de0 | ||
|
|
0cfdc600e7 | ||
|
|
151cfd85ed | ||
|
|
b72df1d64c | ||
|
|
d062b8c32f | ||
|
|
4749163d9c | ||
|
|
bc2a2a72be | ||
|
|
2538f46dfd | ||
|
|
bb9fc8ba83 | ||
|
|
794caced01 | ||
|
|
d473a7e187 | ||
|
|
43ccc76960 | ||
|
|
e38766f0fa | ||
|
|
8b6d0f27d8 | ||
|
|
22593cd544 | ||
|
|
719119b793 | ||
|
|
edd1665c55 | ||
|
|
b803021ce2 | ||
|
|
8992a1c7bc | ||
|
|
626440523d | ||
|
|
116c965cb4 | ||
|
|
3b2e2a2232 | ||
|
|
0b36981c3c | ||
|
|
4a70219ba4 | ||
|
|
ca12b927b5 | ||
|
|
58cb265c61 | ||
|
|
777b7e7aaf | ||
|
|
29afda8b3a | ||
|
|
6e4bedca2d | ||
|
|
e0df785ece | ||
|
|
cd5188c71e | ||
|
|
715fe59ee5 | ||
|
|
8c318fce0e | ||
|
|
4fd5b38832 | ||
|
|
68d186e686 | ||
|
|
8210c4f05d | ||
|
|
f17af9a920 | ||
|
|
b35c692e3b | ||
|
|
160114a364 | ||
|
|
83cf1b6990 | ||
|
|
2157bbf05f | ||
|
|
be8c584e84 | ||
|
|
5b14794690 | ||
|
|
e2687af6ef | ||
|
|
0ac1724f05 | ||
|
|
a52b0b4115 | ||
|
|
ba78be247e | ||
|
|
e0e1a2480d | ||
|
|
7a2ebe7f13 | ||
|
|
2291e35358 | ||
|
|
9876611eef | ||
|
|
7e29a2f4ee | ||
|
|
3fff765adf | ||
|
|
b543965f35 | ||
|
|
68cda2cd46 | ||
|
|
6879c17edd | ||
|
|
dda32c3a40 | ||
|
|
1f07a199db | ||
|
|
c495db4bc6 | ||
|
|
950028af92 | ||
|
|
089bdc64a0 | ||
|
|
32628ec43f | ||
|
|
139a91b094 | ||
|
|
18111aa744 | ||
|
|
857fe1d950 | ||
|
|
e46d104cc7 | ||
|
|
560dd8fddc | ||
|
|
a29b6d172a | ||
|
|
4b3afb5223 | ||
|
|
ca083963e9 | ||
|
|
33c1fdf629 | ||
|
|
a8793dbbc5 | ||
|
|
3ac7ee4b76 | ||
|
|
7e6b3c6ba5 | ||
|
|
137673c551 | ||
|
|
f22829ef4f | ||
|
|
9abb48526b | ||
|
|
57984fe2d6 | ||
|
|
98e284074c | ||
|
|
99e3f5d95e | ||
|
|
8ba1340e92 | ||
|
|
511cceb9d9 | ||
|
|
64e6b8387a | ||
|
|
b8592d35c0 | ||
|
|
cc76eb9d2b | ||
|
|
4189d20961 | ||
|
|
98a9801644 | ||
|
|
63be7c8743 | ||
|
|
7d5b3463cc | ||
|
|
eee2cb1b10 | ||
|
|
36b8e5903d | ||
|
|
9301506d82 | ||
|
|
bba2f7b570 | ||
|
|
f989880d48 | ||
|
|
b083de710b | ||
|
|
ce8eea7b78 | ||
|
|
196ccb29fc | ||
|
|
a92948ee7e | ||
|
|
9f2989fb62 | ||
|
|
1ab5b189dc | ||
|
|
a493e5a4d1 | ||
|
|
a8e679bf03 | ||
|
|
a61eb22b62 | ||
|
|
b93e759ba6 | ||
|
|
66e0acbb8f | ||
|
|
e75948ec01 | ||
|
|
a3ef3ae60d | ||
|
|
a021090dc9 | ||
|
|
3c87ce0fa3 | ||
|
|
86417d4781 | ||
|
|
7c52d622ba | ||
|
|
ab183d89eb | ||
|
|
4b98fa70c0 | ||
|
|
96cb2875c0 | ||
|
|
74ee33ee4c | ||
|
|
a6c7d443ae | ||
|
|
551fc98498 | ||
|
|
ecd2624d2b | ||
|
|
b4629a86e4 | ||
|
|
af28ddf201 | ||
|
|
2f2cd58c30 | ||
|
|
ed3bdf1586 | ||
|
|
d4ff1f97a9 | ||
|
|
f662ec0e67 | ||
|
|
9483fbeb5d | ||
|
|
fd6d384ba2 | ||
|
|
6d69cd3a48 | ||
|
|
ea8ec97f49 | ||
|
|
83a138f38c | ||
|
|
bed4f1ae13 | ||
|
|
aaeb90372e | ||
|
|
669f412869 | ||
|
|
62053d9a34 | ||
|
|
e4e05e2013 | ||
|
|
1fb920e039 | ||
|
|
b35762b1ec | ||
|
|
17498dbc07 | ||
|
|
d52fe75e43 | ||
|
|
aca5ad0c4b | ||
|
|
b6ed7da0a7 | ||
|
|
2abe7d688d | ||
|
|
ce794fe0be | ||
|
|
d81149dd15 | ||
|
|
f9c659612a | ||
|
|
fe9723fa15 | ||
|
|
4914c22b5d | ||
|
|
8961841847 | ||
|
|
554023c839 | ||
|
|
71ce9fea5a | ||
|
|
24aa355ed0 | ||
|
|
3273c1abe5 | ||
|
|
303213c48b | ||
|
|
744983cfc4 | ||
|
|
3a16715e03 | ||
|
|
a360301759 | ||
|
|
c212acd90b | ||
|
|
b374437fb6 | ||
|
|
fc8eda07f7 | ||
|
|
9149f77a37 | ||
|
|
fc747792a1 | ||
|
|
035a63f19e | ||
|
|
4382d8ca73 | ||
|
|
e1de247dbb | ||
|
|
d617dbfc03 | ||
|
|
0b0f9e80e4 | ||
|
|
10cc40ec5a | ||
|
|
8859587768 | ||
|
|
070ccc4fbb | ||
|
|
f80f87d77d | ||
|
|
093966f692 | ||
|
|
76184c0251 | ||
|
|
7b6e767ccb | ||
|
|
8ac3211a86 | ||
|
|
ef38bd7aa3 | ||
|
|
7e98ad725d | ||
|
|
ba72727fba | ||
|
|
19239655ed | ||
|
|
ee59fbf8d2 | ||
|
|
f7e47d2167 | ||
|
|
a979d532d5 | ||
|
|
9ff5dee979 | ||
|
|
edf525f269 | ||
|
|
1b174d5f2f | ||
|
|
9ff5982a5b | ||
|
|
5fb5652f3d | ||
|
|
dbbeb063e7 | ||
|
|
aa8c944fe9 | ||
|
|
21ac9bb45b | ||
|
|
0b3348ee77 | ||
|
|
6574d45939 | ||
|
|
ffd8a54d1e | ||
|
|
601a7f9310 | ||
|
|
1dbcf92336 | ||
|
|
a727d6f6ea | ||
|
|
e6f173df37 | ||
|
|
f4f88da945 | ||
|
|
aedafe1f4b | ||
|
|
e7f8769f61 | ||
|
|
ee809e1fe7 | ||
|
|
bb73d81256 | ||
|
|
ffbb96301e | ||
|
|
eb4f7c0d7c | ||
|
|
f60af287d3 | ||
|
|
1f97fd27a0 | ||
|
|
c8bbc2400b | ||
|
|
57142324ed | ||
|
|
0876469030 | ||
|
|
f6dcdb72ec | ||
|
|
cde38e3bb0 | ||
|
|
632abeef87 | ||
|
|
370182e3aa | ||
|
|
4ad101dca1 | ||
|
|
9db56a7fd7 | ||
|
|
d1af83e258 | ||
|
|
0e39ecaa77 | ||
|
|
2c6449b14d | ||
|
|
1cfb58d922 | ||
|
|
d94d448ee4 | ||
|
|
372395575c | ||
|
|
768d4bbab8 | ||
|
|
0922649988 | ||
|
|
39318354d3 | ||
|
|
813f1c70b7 | ||
|
|
6df78e1c9b | ||
|
|
3cf12de6c0 | ||
|
|
46102d1a76 | ||
|
|
841e9578b7 | ||
|
|
31e8c85d99 | ||
|
|
009628c3ee | ||
|
|
7da7bc8606 | ||
|
|
d71066b95e | ||
|
|
5f53da58e7 | ||
|
|
e32e3ff47d | ||
|
|
648997851c | ||
|
|
9e2e19dfa8 | ||
|
|
e571c22891 | ||
|
|
c1a3575d44 | ||
|
|
6382044f1e | ||
|
|
0a553558c4 | ||
|
|
6d8e58c5d8 | ||
|
|
f7134f6e2d | ||
|
|
7bffaa8cc3 | ||
|
|
943924adef | ||
|
|
916b971e77 | ||
|
|
16e0b19df7 | ||
|
|
3175d8ab47 | ||
|
|
4121cc49da | ||
|
|
76c7443d07 | ||
|
|
fa6de0ce67 | ||
|
|
c3bbc05f8d | ||
|
|
e4caeb66b4 | ||
|
|
3bac819760 | ||
|
|
d6674aed3e | ||
|
|
f20c7b15a4 | ||
|
|
17b9bde6e5 | ||
|
|
018c97f497 | ||
|
|
6841da5557 | ||
|
|
f9bf2dcbcd | ||
|
|
e6774023ce | ||
|
|
aac1c7ad43 | ||
|
|
32de52042d | ||
|
|
62e2ca2a64 | ||
|
|
d3ecafe0ea | ||
|
|
e17dcd11c9 | ||
|
|
d82e08281e | ||
|
|
19763bb84d | ||
|
|
ea08b92e8b | ||
|
|
4447d43c89 | ||
|
|
d33ee8a7f0 | ||
|
|
d93875b751 | ||
|
|
90cec0169f | ||
|
|
040b2c2914 | ||
|
|
b62984d540 | ||
|
|
ba5fee3f94 | ||
|
|
28277e3c54 | ||
|
|
79a08a36fa | ||
|
|
f223cd7959 | ||
|
|
6d0f129949 | ||
|
|
8dcd80ce67 | ||
|
|
653a7bf2fa | ||
|
|
d5d67b108f | ||
|
|
02f8d75273 | ||
|
|
74d56f795d | ||
|
|
64bf5681e8 | ||
|
|
d0a486d7e7 | ||
|
|
f6ab3ffd39 | ||
|
|
8eeb122a38 | ||
|
|
0ca918d814 | ||
|
|
455f4e77e0 | ||
|
|
d92f5f6cf6 | ||
|
|
d31a8188f5 | ||
|
|
f2b98ba803 | ||
|
|
4c4ec32261 | ||
|
|
bb5898835d | ||
|
|
4d52059c7d | ||
|
|
0c24bb1937 | ||
|
|
ca10cf257c | ||
|
|
4c91d27cd2 | ||
|
|
d925634e17 | ||
|
|
160c4a8864 | ||
|
|
29d1338a6d | ||
|
|
2aafe438fb | ||
|
|
96d8489170 | ||
|
|
b0148003b7 | ||
|
|
a86edf1497 | ||
|
|
e6dee77695 | ||
|
|
4da7e07703 | ||
|
|
47ef82aca1 | ||
|
|
e05fcf07b5 | ||
|
|
1f691f1d7f | ||
|
|
9df87b827b | ||
|
|
0fdbd9052a | ||
|
|
cbfce19f73 | ||
|
|
94ad930187 | ||
|
|
afa34f7bcd | ||
|
|
39822883d2 | ||
|
|
0592e0f653 | ||
|
|
065c2b78d5 | ||
|
|
d9febd8117 | ||
|
|
416d01f6df | ||
|
|
497f7bcd40 | ||
|
|
2f56d2b5ca | ||
|
|
e6195302e2 | ||
|
|
551f0753e8 | ||
|
|
d16e345064 | ||
|
|
cc6e2e626b | ||
|
|
00ddcfe6f1 | ||
|
|
2a01876c9e | ||
|
|
ae6936d3c2 | ||
|
|
bdf344fc2e | ||
|
|
e47d7e48d9 | ||
|
|
f297defea8 | ||
|
|
de3ce19eb9 | ||
|
|
57c57b7243 | ||
|
|
18080f878f | ||
|
|
3721e6ab40 | ||
|
|
eb6a79cf97 | ||
|
|
4ff7973d43 | ||
|
|
4472ea7832 | ||
|
|
14f0fb91ea | ||
|
|
1ed504154b | ||
|
|
8c7cd6f0de | ||
|
|
938938f053 | ||
|
|
c2bdb129e6 | ||
|
|
0908837392 | ||
|
|
ef116a9a53 | ||
|
|
f713c24ff8 | ||
|
|
5505bf238d | ||
|
|
34b659693e | ||
|
|
bb9dde869e | ||
|
|
7fc4e2f807 | ||
|
|
dccf79dc55 | ||
|
|
a9635b49ef | ||
|
|
d4d9a5d30f | ||
|
|
a6f0a8723c | ||
|
|
8ccbff16a9 | ||
|
|
96600b6616 | ||
|
|
b782c0aec1 | ||
|
|
7217b73e4a | ||
|
|
5e9db8eb3c | ||
|
|
6e65ad615b | ||
|
|
9345d80b17 | ||
|
|
f8caecf221 | ||
|
|
a452931ccd | ||
|
|
6348be150a | ||
|
|
359a0344f8 | ||
|
|
63987a0585 | ||
|
|
1302274bba | ||
|
|
c5c99a6f12 | ||
|
|
ded3999a12 | ||
|
|
6d856d1463 | ||
|
|
9450e3468b | ||
|
|
96009cecd9 | ||
|
|
51ee3773f7 | ||
|
|
8a92c97485 | ||
|
|
b4e2ded876 | ||
|
|
9b30b0d30a | ||
|
|
35630621b6 | ||
|
|
c5cd7a2f1d | ||
|
|
562c82dd62 | ||
|
|
9677dcb6bd | ||
|
|
08a296b867 | ||
|
|
aae0025047 | ||
|
|
0d882d31ad | ||
|
|
e630e636ad | ||
|
|
092396cc07 | ||
|
|
0b346cc0cb | ||
|
|
6493cc359f | ||
|
|
2774016518 | ||
|
|
cfc2c84409 | ||
|
|
b7bdb67eb4 | ||
|
|
f38d6221c6 | ||
|
|
b8e354b56c | ||
|
|
1a3c535e89 | ||
|
|
d4ced2f734 | ||
|
|
026233c804 | ||
|
|
2673e745f4 | ||
|
|
c3dd2cb44d | ||
|
|
a2f952ff0e | ||
|
|
7c9d773c73 | ||
|
|
8379a4a9bf | ||
|
|
c73e4f1e29 | ||
|
|
1b8c0e2749 | ||
|
|
131b86c93a | ||
|
|
51c7e414ff | ||
|
|
ca700b82c2 | ||
|
|
8db0a03081 | ||
|
|
77862a55f6 | ||
|
|
49661297ab | ||
|
|
17629f1f15 | ||
|
|
aacf36babd | ||
|
|
2b9fca8485 | ||
|
|
f4a0624470 | ||
|
|
08e5e34815 | ||
|
|
846f4aa78f | ||
|
|
e18ddb2e18 | ||
|
|
52ffc2ac28 | ||
|
|
03685ee390 | ||
|
|
4428cc2232 | ||
|
|
1e40d759fe | ||
|
|
485ba7b8ac | ||
|
|
901f75a499 | ||
|
|
4091346ae3 | ||
|
|
e4d22db19d | ||
|
|
399e9c8f1d | ||
|
|
828830c660 | ||
|
|
331952a8f5 | ||
|
|
9c2ed05bab | ||
|
|
d72ff58fe7 | ||
|
|
b94cd7d1a1 | ||
|
|
b5f9261504 | ||
|
|
15abceefeb | ||
|
|
421b860da5 | ||
|
|
03fda24c03 | ||
|
|
207c9dde45 | ||
|
|
f070833144 | ||
|
|
5048a20760 | ||
|
|
3ef7ba9fdf | ||
|
|
91ab03c0ed | ||
|
|
f1c8082252 | ||
|
|
d367eb46f0 | ||
|
|
ecea6c04e9 | ||
|
|
60ee31a074 | ||
|
|
9218553df5 | ||
|
|
7d552b4089 | ||
|
|
8660c06097 | ||
|
|
3e7021b93e | ||
|
|
5dc31e46bf | ||
|
|
ba760ab52b | ||
|
|
bd33a9d1df | ||
|
|
500cfafca3 | ||
|
|
f7b39ab754 | ||
|
|
5c2c5321bc | ||
|
|
217f3112e4 | ||
|
|
add71a9a7e | ||
|
|
ec97df9a59 | ||
|
|
1705fb7b80 | ||
|
|
a232da999d | ||
|
|
d3b965ec38 | ||
|
|
4a8b6e4f66 | ||
|
|
33828dd155 | ||
|
|
63a0c1e806 | ||
|
|
1865b26b58 | ||
|
|
a1a8f90fdb | ||
|
|
432c571f55 | ||
|
|
abc6f7b22e | ||
|
|
da5bf3ce40 | ||
|
|
3eb3976b54 | ||
|
|
0c5bde5a89 | ||
|
|
2d7722c19f | ||
|
|
95c160ca55 | ||
|
|
df302a799c | ||
|
|
5818cd4375 | ||
|
|
2ed62021f6 | ||
|
|
8872d419e2 | ||
|
|
434b0f30e8 | ||
|
|
f20a781c0b | ||
|
|
8ab8adb400 | ||
|
|
ec2591bff2 | ||
|
|
4cb8a57494 | ||
|
|
35d5e36421 | ||
|
|
99a93d77b3 | ||
|
|
d0f528892b | ||
|
|
2d7b97986f | ||
|
|
537c303a18 | ||
|
|
a7ac83cbd3 | ||
|
|
3db3e61e81 | ||
|
|
507054c9ff | ||
|
|
de42d38b31 | ||
|
|
2935181452 | ||
|
|
f04808c709 | ||
|
|
ce0938ea11 | ||
|
|
c356bc76f4 | ||
|
|
adbfb95012 | ||
|
|
dd7e94cde8 | ||
|
|
e337a34cd7 | ||
|
|
98be918c0f | ||
|
|
3778c80fd6 | ||
|
|
58986eb52e | ||
|
|
ef0a14ba63 | ||
|
|
6e9acd781f | ||
|
|
6cbdddcd61 | ||
|
|
03977fb928 | ||
|
|
aaecb99d36 | ||
|
|
4981220d83 | ||
|
|
ed7255ea0e | ||
|
|
a0a85df559 | ||
|
|
b1c4d5bad0 | ||
|
|
0e8e1edb8e | ||
|
|
f0bfc6cbf8 | ||
|
|
8b10dd3e3d | ||
|
|
c0ff6d7e08 | ||
|
|
7981d4a257 | ||
|
|
c83cbeeb22 | ||
|
|
3250c07a16 | ||
|
|
954db9ad07 | ||
|
|
c1e5b5b1e5 | ||
|
|
e5f9b50d21 | ||
|
|
c9e9dd2854 | ||
|
|
1ca08c33fd | ||
|
|
5daf1124a1 | ||
|
|
9d7d66fc99 | ||
|
|
62412c6d9f | ||
|
|
6f1c834e48 | ||
|
|
ecfb4ff31e | ||
|
|
76368ca861 | ||
|
|
3b6788fc16 | ||
|
|
4887224e45 | ||
|
|
9d20120497 | ||
|
|
2ad2fc4023 | ||
|
|
b8e4f9659a | ||
|
|
571df9cd06 | ||
|
|
3a740330ec | ||
|
|
46c6ca51cf | ||
|
|
eb11192ab7 | ||
|
|
2d5453a532 | ||
|
|
7173808ce4 | ||
|
|
c731f706f8 | ||
|
|
b965ae338c | ||
|
|
d4f2308d77 | ||
|
|
f0a1721069 | ||
|
|
c054033c54 | ||
|
|
441d8ed35e | ||
|
|
7643d11f74 | ||
|
|
ab3a53a4ec | ||
|
|
a69f639541 | ||
|
|
0471d99d13 | ||
|
|
4c453e22be | ||
|
|
2ad777025a | ||
|
|
22da4860f7 | ||
|
|
0e14b02109 | ||
|
|
e034f7c1b6 | ||
|
|
279fff4395 | ||
|
|
13d501b75b | ||
|
|
3b6f5a0abf | ||
|
|
d5266070e7 | ||
|
|
def7a08855 | ||
|
|
619e0828af | ||
|
|
6957a876e8 | ||
|
|
75bbb2399c | ||
|
|
c7baed8395 | ||
|
|
c02b4e35ba | ||
|
|
f32c6fcac8 | ||
|
|
e77d995b35 | ||
|
|
e40d03114f | ||
|
|
4e332163e2 | ||
|
|
df59469aa3 | ||
|
|
bf8523b948 | ||
|
|
ff9eccc38c | ||
|
|
48779742e3 | ||
|
|
01a09a5f90 | ||
|
|
aacb1af6b5 | ||
|
|
ae468b4de5 | ||
|
|
fc8cbf0f07 | ||
|
|
af4df5e64f | ||
|
|
0732063e18 | ||
|
|
374d96053b | ||
|
|
872e4b17b4 | ||
|
|
467d862215 | ||
|
|
e91620d17f | ||
|
|
37d0d70482 | ||
|
|
dc781447a8 | ||
|
|
15e5fb6951 | ||
|
|
133c1484f6 | ||
|
|
3e310724ff | ||
|
|
4b528280ba | ||
|
|
146b32b215 | ||
|
|
1330acbf22 | ||
|
|
5592e9dba5 | ||
|
|
75f6fac284 | ||
|
|
71c65d7354 | ||
|
|
040b45394a | ||
|
|
aa645cdfd2 | ||
|
|
153e2a63e0 | ||
|
|
fa619b59d6 | ||
|
|
255621073d | ||
|
|
5580bcde0b | ||
|
|
97c50d588f | ||
|
|
aa8916aea5 | ||
|
|
9bc578fbbe | ||
|
|
d3cbd442e7 | ||
|
|
1ce28fd500 | ||
|
|
e12359fdaa | ||
|
|
9feb4f0fcd | ||
|
|
cfa7a8b640 | ||
|
|
0202192fc0 | ||
|
|
76c3db3e5f | ||
|
|
37a6ee08dc | ||
|
|
55488ae292 | ||
|
|
a5c7db711c | ||
|
|
62d6095edb | ||
|
|
9f653d0371 | ||
|
|
d401a6c33b | ||
|
|
2239f47d45 | ||
|
|
f7f0b07361 | ||
|
|
d43e0b3e59 | ||
|
|
c015ce6ce3 | ||
|
|
772b82857b | ||
|
|
e2cdcbd5ce | ||
|
|
b5ff6695e1 | ||
|
|
8f5d7f1e7f | ||
|
|
70e6836f35 | ||
|
|
40bf54aec1 | ||
|
|
3a46607c0f | ||
|
|
11eb619fc6 | ||
|
|
54ce29d33e | ||
|
|
82807b2c5c | ||
|
|
ddd44e7753 | ||
|
|
e5d2f75606 | ||
|
|
9da17585e8 | ||
|
|
20185d0fad | ||
|
|
accdcc885d | ||
|
|
0323dd3a91 | ||
|
|
293e8eb6c5 | ||
|
|
b175ea18e2 | ||
|
|
2fb5bc8f5b | ||
|
|
90c96791be | ||
|
|
ff9834cf24 | ||
|
|
530d0ee14c | ||
|
|
4aedc1d481 | ||
|
|
950e54c990 | ||
|
|
b2960285bf | ||
|
|
ad7f7fcc37 | ||
|
|
2fc08a1c6c | ||
|
|
3954bbe862 | ||
|
|
2d028b4969 | ||
|
|
93047e67c0 | ||
|
|
66ef51a47d | ||
|
|
4dd54bab8e | ||
|
|
a2416003d6 | ||
|
|
7e14d61a34 | ||
|
|
0cb953af85 | ||
|
|
dc781a04c0 | ||
|
|
234f7bcac0 | ||
|
|
58c10e201a | ||
|
|
28892b3933 |
357 changed files with 21996 additions and 32161 deletions
|
|
@ -1,17 +0,0 @@
|
||||||
platform: x64
|
|
||||||
|
|
||||||
install:
|
|
||||||
- ps: Install-Product node $env:nodejs_version $env:platform
|
|
||||||
|
|
||||||
build: off
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- node --version
|
|
||||||
- npm --version
|
|
||||||
|
|
||||||
- npm i now nupdate -g
|
|
||||||
- nupdate -a gritty
|
|
||||||
- now rm cloudcmd -y -t %NOW_TOKEN%
|
|
||||||
- now --public -t %NOW_TOKEN%
|
|
||||||
- now alias -t %NOW_TOKEN%
|
|
||||||
|
|
||||||
10
.babelrc
10
.babelrc
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"transform-object-assign",
|
|
||||||
"transform-object-rest-spread"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.bowerrc
3
.bowerrc
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"directory" : "modules"
|
|
||||||
}
|
|
||||||
4
.browserslistrc
Normal file
4
.browserslistrc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
last 2 Chrome versions
|
||||||
|
last 2 Safari versions
|
||||||
|
Firefox ESR
|
||||||
|
not dead
|
||||||
31
.cloudcmd.menu.js
Normal file
31
.cloudcmd.menu.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
export default {
|
||||||
|
'F2 - Rename File': renameCurrent,
|
||||||
|
'L - Lint': run('npm run lint'),
|
||||||
|
'F - Fix Lint': run('npm run fix:lint'),
|
||||||
|
'T - Test': run('npm run test'),
|
||||||
|
'C - Coverage': run('npm run coverage'),
|
||||||
|
'D - Build Dev': run('npm run build:client:dev'),
|
||||||
|
'P - Build Prod': run('npm run build:client'),
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -11,27 +11,13 @@ yarn-error.log
|
||||||
yarn.lock
|
yarn.lock
|
||||||
now.json
|
now.json
|
||||||
|
|
||||||
Dockerfile
|
docker
|
||||||
Dockerfile.alpine
|
|
||||||
|
|
||||||
webpack.config.js
|
webpack.config.js
|
||||||
|
cssnano.config.js
|
||||||
|
|
||||||
bin/release.js
|
bin/release.js
|
||||||
|
|
||||||
modules/jquery/dist
|
|
||||||
modules/jquery/external
|
|
||||||
modules/jquery/src
|
|
||||||
|
|
||||||
!modules/jquery/dist/jquery.min.js
|
|
||||||
|
|
||||||
modules/fancybox/lib/
|
|
||||||
modules/fancybox/demo
|
|
||||||
modules/fancybox/sprite.psd
|
|
||||||
|
|
||||||
modules/execon
|
|
||||||
modules/emitify
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
|
||||||
22
.eslintrc
22
.eslintrc
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"es6": true,
|
|
||||||
"node": true,
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2017,
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"experimentalObjectRestSpread": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"indent": ["error", 4],
|
|
||||||
"semi": "error",
|
|
||||||
"no-console": 0,
|
|
||||||
"no-use-before-define": ["error", "nofunc"]
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-process-exit": 0
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
".eslintrc",
|
|
||||||
"plugin:node/recommended"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
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
|
||||||
53
.github/workflows/nodejs.yml
vendored
Normal file
53
.github/workflows/nodejs.yml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Node CI
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
NAME: cloudcmd
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version:
|
||||||
|
- 22.x
|
||||||
|
- 24.x
|
||||||
|
- 26.x
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- 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 }}
|
||||||
35
.gitignore
vendored
35
.gitignore
vendored
|
|
@ -1,30 +1,17 @@
|
||||||
package-lock.json
|
*.swp
|
||||||
yarn.lock
|
*.log
|
||||||
yarn-error.log
|
*.lock
|
||||||
node_modules
|
|
||||||
npm-debug.log*
|
|
||||||
coverage
|
|
||||||
|
|
||||||
modules/jquery/dist
|
|
||||||
modules/jquery/external
|
|
||||||
modules/jquery/src
|
|
||||||
|
|
||||||
!modules/jquery/dist/jquery.min.js
|
|
||||||
|
|
||||||
modules/fancybox/lib/
|
|
||||||
modules/fancybox/demo
|
|
||||||
modules/fancybox/sprite.psd
|
|
||||||
modules/fancybox/*.json
|
|
||||||
modules/fancybox/gulpfile.js
|
|
||||||
modules/jquery-mousewheel
|
|
||||||
|
|
||||||
modules/execon
|
|
||||||
modules/emitify
|
|
||||||
|
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
|
||||||
*.swp
|
package-lock.json
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
coverage
|
||||||
|
modules/execon
|
||||||
|
modules/emitify
|
||||||
dist
|
dist
|
||||||
dist-dev
|
dist-dev
|
||||||
|
|
||||||
|
|
|
||||||
62
.madrun.js
Normal file
62
.madrun.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import {run, cutEnv} from 'madrun';
|
||||||
|
import {defineEnv} from 'supertape/env';
|
||||||
|
|
||||||
|
const testEnv = defineEnv({
|
||||||
|
timeout: 7000,
|
||||||
|
css: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildEnv = {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'start': () => 'node bin/cloudcmd.js',
|
||||||
|
'start:dev': async () => await run('start', null, {
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
}),
|
||||||
|
'build:start': () => run(['build:client', 'start']),
|
||||||
|
'build:start:dev': () => run([
|
||||||
|
'build:client:dev',
|
||||||
|
'start:dev',
|
||||||
|
]),
|
||||||
|
'lint:all': () => run('lint:progress'),
|
||||||
|
'lint': () => 'redlint fix; putout .',
|
||||||
|
'lint:progress': () => run('lint', '-f progress'),
|
||||||
|
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
|
||||||
|
'fresh:lint': () => run('lint', '--fresh'),
|
||||||
|
'lint:fresh': () => run('lint', '--fresh'),
|
||||||
|
'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:e2e': () => `tape 'test-e2e/**/*.js'`,
|
||||||
|
'test:client': () => `tape 'test/client/**/*.js'`,
|
||||||
|
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
||||||
|
'wisdom': async () => await run(['lint:all', 'build', 'test'], null, {
|
||||||
|
CI: 1,
|
||||||
|
}),
|
||||||
|
'wisdom:type': () => 'bin/release.js',
|
||||||
|
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
|
||||||
|
'coverage:report': () => 'c8 report --reporter=lcov',
|
||||||
|
'report': () => 'c8 report --reporter=lcov',
|
||||||
|
'6to5': () => [buildEnv, 'rspack build --config rspack.config.js'],
|
||||||
|
'6to5:client': () => run('6to5', '--mode production'),
|
||||||
|
'6to5:client:dev': async () => await run('6to5', '--mode development', {
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
}),
|
||||||
|
'pre6to5:client': () => 'rimraf dist',
|
||||||
|
'pre6to5:client:dev': () => 'rimraf dist-dev',
|
||||||
|
'watch:client': () => run('6to5:client', '--watch'),
|
||||||
|
'watch:client:dev': () => run('6to5:client:dev', '--watch'),
|
||||||
|
'watch:server': () => 'nodemon bin/cloudcmd.js',
|
||||||
|
'watch:test': async () => [testEnv, `nodemon -w client -w server -w test -w common -x ${await cutEnv('test')}`],
|
||||||
|
'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`,
|
||||||
|
'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`,
|
||||||
|
'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`],
|
||||||
|
'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`,
|
||||||
|
'build': async () => run('6to5:*'),
|
||||||
|
'build:dev': async () => run('build:client:dev'),
|
||||||
|
'build:client': () => run('6to5:client'),
|
||||||
|
'build:client:dev': () => run('6to5:client:dev'),
|
||||||
|
'heroku-postbuild': () => run('6to5:client'),
|
||||||
|
};
|
||||||
48
.npmignore
48
.npmignore
|
|
@ -1,33 +1,29 @@
|
||||||
|
*.spec.*
|
||||||
|
*.config.*
|
||||||
|
*.fixture.js*
|
||||||
|
*.ai
|
||||||
|
*.cdr
|
||||||
|
*.eps
|
||||||
|
*.log
|
||||||
|
*.lock
|
||||||
|
|
||||||
.*
|
.*
|
||||||
*.spec.js
|
|
||||||
manifest.yml
|
manifest.yml
|
||||||
Dockerfile*
|
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
test
|
now.json
|
||||||
|
app.json
|
||||||
|
bower.json
|
||||||
|
deno.json
|
||||||
|
bin/release.*
|
||||||
|
img/logo/cloudcmd-hq.png
|
||||||
|
webpack.config.js
|
||||||
|
|
||||||
|
docker
|
||||||
|
test*
|
||||||
|
fixture
|
||||||
|
fixture-*
|
||||||
coverage
|
coverage
|
||||||
css
|
css
|
||||||
html
|
html
|
||||||
yarn-error.log
|
|
||||||
yarn.lock
|
|
||||||
now.json
|
|
||||||
|
|
||||||
modules/jquery/dist
|
|
||||||
modules/jquery/external
|
|
||||||
modules/jquery/src
|
|
||||||
|
|
||||||
!modules/jquery/dist/jquery.min.js
|
|
||||||
|
|
||||||
modules/fancybox/lib/
|
|
||||||
modules/fancybox/demo
|
|
||||||
modules/fancybox/sprite.psd
|
|
||||||
|
|
||||||
app.json
|
|
||||||
bower.json
|
|
||||||
manifest.yml
|
|
||||||
|
|
||||||
bin/release.js
|
|
||||||
|
|
||||||
client
|
client
|
||||||
|
|
||||||
webpack.config.js
|
|
||||||
|
|
||||||
|
|
|
||||||
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
|
||||||
|
}
|
||||||
60
.putout.json
Normal file
60
.putout.json
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"plugins": ["cloudcmd"],
|
||||||
|
"ignore": [
|
||||||
|
"*.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": {
|
||||||
|
"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,16 +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
|
|
||||||
|
|
||||||
27
.travis.yml
27
.travis.yml
|
|
@ -1,27 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 10
|
|
||||||
- 8
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
provider: script
|
|
||||||
script: npm run docker-ci
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
node: '8'
|
|
||||||
tags: true
|
|
||||||
all_branches: false
|
|
||||||
|
|
||||||
2
.typos.toml
Normal file
2
.typos.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[files]
|
||||||
|
extend-exclude = ["ChangeLog"]
|
||||||
|
|
@ -1,72 +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 extractCSS = (a) => new ExtractTextPlugin(`${a}.css`);
|
|
||||||
const extractMain = extractCSS('[name]');
|
|
||||||
|
|
||||||
const cssNames = [
|
|
||||||
'nojs',
|
|
||||||
'view',
|
|
||||||
'config',
|
|
||||||
...getCSSList('columns'),
|
|
||||||
];
|
|
||||||
|
|
||||||
const cssPlugins = cssNames.map(extractCSS);
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
...cssPlugins,
|
|
||||||
extractMain,
|
|
||||||
];
|
|
||||||
|
|
||||||
const rules = [{
|
|
||||||
test: /\.css$/,
|
|
||||||
exclude: /css\/(nojs|view|config|columns.*)\.css/,
|
|
||||||
use: extractMain.extract([
|
|
||||||
'css-loader?minimize',
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
...cssPlugins.map(extract), {
|
|
||||||
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
|
||||||
loader: 'url-loader?limit=50000',
|
|
||||||
}];
|
|
||||||
|
|
||||||
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;
|
|
||||||
const loader = isDev ? 'css-loader' : 'css-loader?minimize';
|
|
||||||
|
|
||||||
return {
|
|
||||||
test: RegExp(`css/${filename}`),
|
|
||||||
use: extractPlugin.extract([
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const {
|
|
||||||
resolve,
|
|
||||||
sep,
|
|
||||||
join,
|
|
||||||
} = require('path');
|
|
||||||
|
|
||||||
const dir = './client';
|
|
||||||
const dirModules = './client/modules';
|
|
||||||
const modules = './modules';
|
|
||||||
|
|
||||||
const {env} = process;
|
|
||||||
const isDev = env.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 rules = clean([
|
|
||||||
!isDev && {
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
}]);
|
|
||||||
|
|
||||||
const splitChunks = {
|
|
||||||
chunks: 'all',
|
|
||||||
name: 'cloudcmd.common',
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
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 + '/menu']: `${dirModules}/menu.js`,
|
|
||||||
[modules + '/view']: `${dirModules}/view.js`,
|
|
||||||
[modules + '/help']: `${dirModules}/help.js`,
|
|
||||||
[modules + '/markdown']: `${dirModules}/markdown.js`,
|
|
||||||
[modules + '/config']: `${dirModules}/config.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 + '/cloud']: `${dirModules}/cloud.js`,
|
|
||||||
[modules + '/polyfill']: `${dirModules}/polyfill.js`,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: '[name].js',
|
|
||||||
path: isDev ? distDev : dist,
|
|
||||||
pathinfo: isDev,
|
|
||||||
devtoolModuleFilenameTemplate,
|
|
||||||
publicPath: '/dist/',
|
|
||||||
},
|
|
||||||
externals: [
|
|
||||||
externals
|
|
||||||
],
|
|
||||||
module: {
|
|
||||||
rules,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function externals(context, request, fn) {
|
|
||||||
if (!isDev)
|
|
||||||
return fn();
|
|
||||||
|
|
||||||
const list = [
|
|
||||||
'es6-promise',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (list.includes(request))
|
|
||||||
return fn(null, request);
|
|
||||||
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
|
|
||||||
function devtoolModuleFilenameTemplate(info) {
|
|
||||||
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
|
|
||||||
return `file://cloudcmd/${resource}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
18
.yaspellerrc
18
.yaspellerrc
|
|
@ -10,6 +10,7 @@
|
||||||
".md"
|
".md"
|
||||||
],
|
],
|
||||||
"dictionary":[
|
"dictionary":[
|
||||||
|
"CloudCmd",
|
||||||
"Dev",
|
"Dev",
|
||||||
"Dropbox",
|
"Dropbox",
|
||||||
"Deepword",
|
"Deepword",
|
||||||
|
|
@ -21,17 +22,23 @@
|
||||||
"Iptables",
|
"Iptables",
|
||||||
"JitSu",
|
"JitSu",
|
||||||
"Node",
|
"Node",
|
||||||
|
"IO",
|
||||||
"Olena",
|
"Olena",
|
||||||
"TarZak",
|
"TarZak",
|
||||||
"Termux",
|
"Termux",
|
||||||
"Zalitok",
|
"Zalitok",
|
||||||
"WebSocket",
|
"WebSocket",
|
||||||
|
"auth",
|
||||||
|
"autostart",
|
||||||
|
"binded",
|
||||||
"cd",
|
"cd",
|
||||||
"cloudcmd",
|
"cloudcmd",
|
||||||
"coderaiser",
|
"coderaiser",
|
||||||
"com",
|
"com",
|
||||||
"dev",
|
|
||||||
"deepword",
|
"deepword",
|
||||||
|
"dev",
|
||||||
|
"destructuring",
|
||||||
|
"dropbox",
|
||||||
"dword",
|
"dword",
|
||||||
"edward",
|
"edward",
|
||||||
"favicon",
|
"favicon",
|
||||||
|
|
@ -39,12 +46,18 @@
|
||||||
"gz",
|
"gz",
|
||||||
"io",
|
"io",
|
||||||
"js",
|
"js",
|
||||||
|
"linux",
|
||||||
"maintainers",
|
"maintainers",
|
||||||
|
"markdown",
|
||||||
|
"microservice",
|
||||||
"minification",
|
"minification",
|
||||||
"mouseup",
|
"mouseup",
|
||||||
"named",
|
"named",
|
||||||
"nginx",
|
"nginx",
|
||||||
|
"npm",
|
||||||
"or io",
|
"or io",
|
||||||
|
"patreon",
|
||||||
|
"rc",
|
||||||
"refactor",
|
"refactor",
|
||||||
"sexualized",
|
"sexualized",
|
||||||
"sslPort",
|
"sslPort",
|
||||||
|
|
@ -53,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,27 +1,30 @@
|
||||||
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
|
||||||
**Message body**:
|
**Message body**:
|
||||||
- just as in <subject> use imperative, present tense: “change” not “changed” nor “changes”
|
- just as in <subject> use imperative, present tense: “change” not “changed” nor “changes”
|
||||||
- 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)
|
||||||
|
|
|
||||||
24
Dockerfile
24
Dockerfile
|
|
@ -1,24 +0,0 @@
|
||||||
FROM node:10.1
|
|
||||||
MAINTAINER Coderaiser
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
COPY package.json /usr/src/app/
|
|
||||||
|
|
||||||
RUN npm config set package-lock false && \
|
|
||||||
npm install --production && \
|
|
||||||
npm i gritty && \
|
|
||||||
npm cache clean --force
|
|
||||||
|
|
||||||
COPY . /usr/src/app
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
ENV cloudcmd_terminal true
|
|
||||||
ENV cloudcmd_terminal_path gritty
|
|
||||||
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/src/app/bin/cloudcmd.js"]
|
|
||||||
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
FROM mhart/alpine-node:10.1
|
|
||||||
MAINTAINER Coderaiser
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
COPY package.json /usr/src/app/
|
|
||||||
|
|
||||||
RUN npm config set package-lock false && \
|
|
||||||
npm install --production && \
|
|
||||||
apk update && \
|
|
||||||
apk add --no-cache bash make g++ python && \
|
|
||||||
npm i gritty && \
|
|
||||||
npm cache clean --force && \
|
|
||||||
apk del make g++ python && \
|
|
||||||
rm -rf /usr/include /tmp/* /var/cache/apk/*
|
|
||||||
|
|
||||||
COPY . /usr/src/app
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
ENV cloudcmd_terminal true
|
|
||||||
ENV cloudcmd_terminal_path gritty
|
|
||||||
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/src/app/bin/cloudcmd.js"]
|
|
||||||
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
(The MIT License)
|
(The MIT License)
|
||||||
|
|
||||||
Copyright (c) 2012-2018 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
|
||||||
|
|
|
||||||
193
README.md
193
README.md
|
|
@ -1,45 +1,33 @@
|
||||||
# Cloud Commander v9.6.0 [![License][LicenseIMGURL]][LicenseURL] [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Now status][BuildAppveyorIMGURL]][BuildAppveyorURL] [![Package Quality][PackageQualityIMGURL]][PackageQualityURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [](#backers) [](#sponsors)
|
# 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], [Now][NowURL])
|
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
|
||||||
|
|
||||||
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
[MainURL]: https://cloudcmd.io "Main"
|
||||||
[MainURL]: http://cloudcmd.io "Main"
|
[BlogURL]: https://blog.cloudcmd.io "Blog"
|
||||||
[BlogURL]: http://blog.cloudcmd.io "Blog"
|
[SupportURL]: https://patreon.com/coderaiser "Patreon"
|
||||||
[HerokuURL]: http://cloudcmd.herokuapp.com/ "Heroku"
|
[DemoURL]: https://cloudcmd-zdp6.onrender.com
|
||||||
[NowURL]: https://cloudcmd.now.sh/ "Now"
|
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
||||||
[NPMURL]: https://npmjs.org/package/cloudcmd "npm"
|
[BuildStatusURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml "Build Status"
|
||||||
[NPMIMGURL]: https://img.shields.io/npm/v/cloudcmd.svg?style=flat
|
[BuildStatusIMGURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml/badge.svg
|
||||||
[LicenseURL]: https://tldrlegal.com/license/mit-license "MIT License"
|
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
||||||
[LicenseIMGURL]: https://img.shields.io/badge/license-MIT-317BF9.svg?style=flat
|
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
||||||
[DependencyStatusURL]: https://david-dm.org/coderaiser/cloudcmd
|
[GitterURL]: https://gitter.im/cloudcmd/hello
|
||||||
[DependencyStatusIMGURL]: https://img.shields.io/david/coderaiser/cloudcmd.svg?style=flat
|
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
||||||
[BuildStatusURL]: https://travis-ci.org/coderaiser/cloudcmd "Build Status"
|
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
||||||
[BuildStatusIMGURL]: https://img.shields.io/travis/coderaiser/cloudcmd.svg?style=flat
|
[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
|
||||||
|
|
||||||
[BuildAppveyorURL]: https://ci.appveyor.com/project/coderaiser/cloudcmd
|
**Cloud Commander** a file manager for the web with console and editor.
|
||||||
[BuildAppveyorIMGURL]: https://ci.appveyor.com/api/projects/status/tse6sc8dxrqxqehi?svg=true
|
|
||||||
|
|
||||||
[PackageQualityURL]: http://packagequality.com/#?package=cloudcmd "Package Quality"
|

|
||||||
[PackageQualityIMGURL]: http://packagequality.com/shield/cloudcmd.svg
|
|
||||||
|
|
||||||
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
|
||||||
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
|
||||||
|
|
||||||
[GitterURL]: https://gitter.im/cloudcmd/hello
|
|
||||||
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
|
||||||
|
|
||||||
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
|
||||||
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
|
|
||||||
|
|
||||||
**Cloud Commander** is an orthodox web file manager with console and editor.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i cloudcmd -g
|
npm i cloudcmd -g
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
|
|
||||||
For starting just type in console:
|
For starting just type in console:
|
||||||
|
|
@ -52,7 +40,13 @@ cloudcmd
|
||||||
|
|
||||||
Open url `http://localhost:8000` in browser.
|
Open url `http://localhost:8000` in browser.
|
||||||
|
|
||||||
|
### View
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
@ -76,33 +70,31 @@ 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 = '/cloudcmd';
|
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 = {
|
||||||
prefix // base URL or function which returns base URL (optional)
|
name: 'cloudcmd :)',
|
||||||
};
|
};
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
__dirname + '/plugin.js'
|
|
||||||
];
|
|
||||||
|
|
||||||
const filePicker = {
|
const filePicker = {
|
||||||
data: {
|
data: {
|
||||||
FilePicker: {
|
FilePicker: {
|
||||||
key: 'key',
|
key: 'key',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// override option from json/modules.json
|
// override option from json/modules.json
|
||||||
|
|
@ -110,22 +102,40 @@ const modules = {
|
||||||
filePicker,
|
filePicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
app.use(cloudcmd({
|
const {
|
||||||
socket, // used by Config, Edit (optional) and Console (required)
|
createConfigManager,
|
||||||
config, // config data (optional)
|
configPath,
|
||||||
plugins, // optional
|
} = cloudcmd;
|
||||||
|
|
||||||
|
const configManager = createConfigManager({
|
||||||
|
configPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(prefix, cloudcmd({
|
||||||
|
socket, // used by Config, Edit (optional) and Console (required)
|
||||||
|
config, // config data (optional)
|
||||||
modules, // optional
|
modules, // optional
|
||||||
|
configManager, // optional
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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 -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`,
|
||||||
|
|
@ -151,84 +161,19 @@ 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");
|
||||||
|
|
||||||
## Backers
|
|
||||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/cloudcmd#backer)]
|
|
||||||
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/0/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/1/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/2/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/3/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/4/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/5/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/6/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/7/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/8/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/9/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/10/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/11/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/12/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/13/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/14/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/15/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/16/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/17/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/18/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/19/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/20/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/21/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/22/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/23/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/24/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/25/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/26/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/27/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/28/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/backer/29/website)
|
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/cloudcmd#sponsor)]
|
|
||||||
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/0/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/1/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/2/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/3/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/4/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/5/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/6/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/7/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/8/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/9/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/10/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/11/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/12/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/13/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/14/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/15/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/16/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/17/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/18/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/19/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/20/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/21/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/22/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/23/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/24/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/25/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/26/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/27/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/28/website)
|
|
||||||
[](https://opencollective.com/cloudcmd/sponsor/29/website)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|
||||||
|
|
|
||||||
63
app.json
63
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",
|
||||||
|
|
@ -32,6 +33,11 @@
|
||||||
"value": "false",
|
"value": "false",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"CLOUDCMD_CONFIG_AUTH": {
|
||||||
|
"description": "disable auth change in config dialog",
|
||||||
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"CLOUDCMD_CONSOLE": {
|
"CLOUDCMD_CONSOLE": {
|
||||||
"description": "enable console",
|
"description": "enable console",
|
||||||
"value": "true",
|
"value": "true",
|
||||||
|
|
@ -52,9 +58,9 @@
|
||||||
"value": "gritty",
|
"value": "gritty",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"CLOUDCMD_CONFIG_DIALOG": {
|
"CLOUDCMD_TERMINAL_COMMAND": {
|
||||||
"description": "show config dialog",
|
"description": "set command to run in terminal (shell by default)",
|
||||||
"value": "false",
|
"value": "",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"CLOUDCMD_ONE_FILE_PANEL": {
|
"CLOUDCMD_ONE_FILE_PANEL": {
|
||||||
|
|
@ -92,16 +98,6 @@
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"CLOUDCMD_ONE_PANEL_MODE": {
|
|
||||||
"description": "set one panel mode (deprecated use CLOUDCMD_ONE_FILE_PANEL)",
|
|
||||||
"value": "false",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"CLOUDCMD_ONE_FILE_PANEL": {
|
|
||||||
"description": "set one panel mode",
|
|
||||||
"value": "false",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"CLOUDCMD_VIM": {
|
"CLOUDCMD_VIM": {
|
||||||
"description": "enable vim hot keys",
|
"description": "enable vim hot keys",
|
||||||
"value": "false",
|
"value": "false",
|
||||||
|
|
@ -112,6 +108,11 @@
|
||||||
"value": "name-size-date-owner-mode",
|
"value": "name-size-date-owner-mode",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"CLOUDCMD_OPEN": {
|
||||||
|
"description": "open web browser when server started",
|
||||||
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"CLOUDCMD_CONFIRM_COPY": {
|
"CLOUDCMD_CONFIRM_COPY": {
|
||||||
"description": "confirm copy",
|
"description": "confirm copy",
|
||||||
"value": "true",
|
"value": "true",
|
||||||
|
|
@ -122,9 +123,39 @@
|
||||||
"value": "true",
|
"value": "true",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"CLOUDCMD_CACHE": {
|
"CLOUDCMD_SHOW_FILE_NAME": {
|
||||||
"description": "enable cache",
|
"description": "show file name in view and edit",
|
||||||
"value": "true",
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_EXPORT": {
|
||||||
|
"description": "enable export of config through a server",
|
||||||
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_EXPORT_TOKEN": {
|
||||||
|
"description": "authorization token used by export server",
|
||||||
|
"value": "root",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_IMPORT": {
|
||||||
|
"description": "enable import of config",
|
||||||
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_IMPORT_TOKEN": {
|
||||||
|
"description": "authorization token used to connect to export server",
|
||||||
|
"value": "root",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_IMPORT_URL": {
|
||||||
|
"description": "url of an import server",
|
||||||
|
"value": "http://localhost:8000",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"CLOUDCMD_IMPORT_LISTEN": {
|
||||||
|
"description": "enable listen on config updates from import server",
|
||||||
|
"value": "false",
|
||||||
"required": false
|
"required": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
375
bin/cloudcmd.js
375
bin/cloudcmd.js
|
|
@ -1,26 +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 util = require('util');
|
process.on('unhandledRejection', exit);
|
||||||
const noop = () => {};
|
|
||||||
|
|
||||||
const Info = require('../package');
|
const isUndefined = (a) => typeof a === 'undefined';
|
||||||
const DIR_SERVER = '../server/';
|
|
||||||
|
|
||||||
const exit = require(DIR_SERVER + 'exit');
|
|
||||||
const config = require(DIR_SERVER + 'config');
|
|
||||||
const env = require(DIR_SERVER + 'env');
|
|
||||||
|
|
||||||
const choose = (a, b) => {
|
const choose = (a, b) => {
|
||||||
if (!a)
|
if (isUndefined(a))
|
||||||
return b;
|
return b;
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
|
|
||||||
const argv = process.argv;
|
const config = createConfig({
|
||||||
const args = require('minimist')(argv.slice(2), {
|
configPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
const maybeRoot = (a) => {
|
||||||
|
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',
|
||||||
|
|
@ -31,77 +53,109 @@ const args = require('minimist')(argv.slice(2), {
|
||||||
'packer',
|
'packer',
|
||||||
'root',
|
'root',
|
||||||
'prefix',
|
'prefix',
|
||||||
|
'prefix-socket',
|
||||||
'terminal-path',
|
'terminal-path',
|
||||||
|
'terminal-command',
|
||||||
'columns',
|
'columns',
|
||||||
|
'menu',
|
||||||
|
'theme',
|
||||||
|
'import-url',
|
||||||
|
'import-token',
|
||||||
|
'export-token',
|
||||||
|
'dropbox-token',
|
||||||
],
|
],
|
||||||
boolean: [
|
boolean: [
|
||||||
'auth',
|
'auth',
|
||||||
'cache',
|
|
||||||
'repl',
|
'repl',
|
||||||
'save',
|
'save',
|
||||||
'server',
|
'server',
|
||||||
'online',
|
'online',
|
||||||
'open',
|
'open',
|
||||||
'progress',
|
|
||||||
'config-dialog',
|
'config-dialog',
|
||||||
|
'config-auth',
|
||||||
|
'config-port',
|
||||||
'console',
|
'console',
|
||||||
'sync-console-path',
|
'sync-console-path',
|
||||||
'contact',
|
'contact',
|
||||||
'terminal',
|
'terminal',
|
||||||
|
'terminal-auto-restart',
|
||||||
'one-file-panel',
|
'one-file-panel',
|
||||||
'one-panel-mode',
|
|
||||||
'confirm-copy',
|
'confirm-copy',
|
||||||
'confirm-move',
|
'confirm-move',
|
||||||
'html-dialogs',
|
|
||||||
'show-config',
|
'show-config',
|
||||||
|
'show-dot-files',
|
||||||
|
'show-file-name',
|
||||||
'vim',
|
'vim',
|
||||||
'keys-panel',
|
'keys-panel',
|
||||||
|
'color',
|
||||||
|
'export',
|
||||||
|
'import',
|
||||||
|
'import-listen',
|
||||||
|
'log',
|
||||||
|
'zip',
|
||||||
|
'dropbox',
|
||||||
],
|
],
|
||||||
default: {
|
default: {
|
||||||
server : true,
|
'server': true,
|
||||||
name : choose(env('name'), config('name')),
|
'name': choose(env.parse('name'), config('name')),
|
||||||
auth : choose(env('auth'), config('auth')),
|
'auth': choose(env.bool('auth'), config('auth')),
|
||||||
cache : choose(env.bool('cache'), config('cache')),
|
'port': config('port'),
|
||||||
port : config('port'),
|
'online': config('online'),
|
||||||
online : config('online'),
|
'open': choose(env.bool('open'), config('open')),
|
||||||
open : config('open'),
|
'editor': env.parse('editor') || config('editor'),
|
||||||
editor : env('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 : config('prefix'),
|
'prefix': choose(env.parse('prefix'), config('prefix')),
|
||||||
progress : config('progress'),
|
'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.parse('columns') || config('columns') || '',
|
||||||
|
'theme': env.parse('theme') || config('theme') || '',
|
||||||
|
'vim': choose(env.bool('vim'), config('vim')),
|
||||||
|
'log': choose(env.bool('log'), config('log')),
|
||||||
|
|
||||||
|
'import-url': env.parse('import_url') || config('importUrl'),
|
||||||
|
'import-listen': choose(env.bool('import_listen'), config('importListen')),
|
||||||
|
'import': choose(env.bool('import'), config('import')),
|
||||||
|
'export': choose(env.bool('export'), config('export')),
|
||||||
|
|
||||||
|
'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')),
|
||||||
'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')),
|
||||||
'terminal-path': env('terminal_path') || config('terminalPath'),
|
'config-auth': choose(env.bool('config_auth'), config('configAuth')),
|
||||||
'one-file-panel': choose(env.bool('one_file_panel'), config('onePanelMode')),
|
'config-port': choose(env.bool('config_port'), config('configPort')),
|
||||||
'one-panel-mode': '',
|
'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')),
|
||||||
|
'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')),
|
||||||
'html-dialogs': config('htmlDialogs'),
|
|
||||||
'vim': choose(env.bool('vim'), config('vim')),
|
|
||||||
'columns': env('columns') || config('columns') || '',
|
|
||||||
'keys-panel': env.bool('keys_panel') || config('keysPanel'),
|
'keys-panel': env.bool('keys_panel') || config('keysPanel'),
|
||||||
|
'import-token': env.parse('import_token') || config('importToken'),
|
||||||
|
'export-token': env.parse('export_token') || config('exportToken'),
|
||||||
|
|
||||||
|
'dropbox': config('dropbox'),
|
||||||
|
'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();
|
||||||
|
|
@ -110,91 +164,112 @@ else if (args.help)
|
||||||
else
|
else
|
||||||
main();
|
main();
|
||||||
|
|
||||||
function main() {
|
async function main() {
|
||||||
if (args.repl)
|
const {validateArgs} = await import('@putout/cli-validate-args');
|
||||||
repl();
|
|
||||||
|
|
||||||
checkUpdate();
|
const error = await validateArgs(args, [
|
||||||
|
...yargsOptions.boolean,
|
||||||
|
...yargsOptions.string,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return exit(error);
|
||||||
|
|
||||||
|
if (args.repl)
|
||||||
|
await repl();
|
||||||
|
|
||||||
|
validate.columns(args.columns);
|
||||||
|
validate.theme(args.theme);
|
||||||
|
|
||||||
port(args.port);
|
port(args.port);
|
||||||
|
|
||||||
config('name', args.name);
|
config('name', args.name);
|
||||||
config('auth', args.auth);
|
config('auth', args.auth);
|
||||||
config('cache', args.cache);
|
|
||||||
config('online', args.online);
|
config('online', args.online);
|
||||||
config('open', args.open);
|
config('open', args.open);
|
||||||
config('username', args.username);
|
config('username', args.username);
|
||||||
config('progress', args.progress);
|
|
||||||
config('console', args.console);
|
config('console', args.console);
|
||||||
config('syncConsolePath', args['sync-console-path']);
|
config('syncConsolePath', args.syncConsolePath);
|
||||||
|
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.terminalCommand);
|
||||||
|
config('terminalAutoRestart', args.terminalAutoRestart);
|
||||||
config('editor', args.editor);
|
config('editor', args.editor);
|
||||||
config('prefix', args.prefix);
|
config('menu', args.menu);
|
||||||
config('root', args.root);
|
config('prefix', prefixer(args.prefix));
|
||||||
|
config('prefixSocket', prefixer(args.prefixSocket));
|
||||||
|
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('htmlDialogs', args['html-dialogs']);
|
config('log', args.log);
|
||||||
config('confirmCopy', args['confirm-copy']);
|
config('confirmCopy', args.confirmCopy);
|
||||||
config('confirmMove', args['confirm-move']);
|
config('confirmMove', args.confirmMove);
|
||||||
config('onePanelMode', args['one-file-panel']);
|
config('oneFilePanel', args.oneFilePanel);
|
||||||
config('oneFilePanel', args['one-file-panel']);
|
config('configDialog', args.configDialog);
|
||||||
config('configDialog', args['config-dialog']);
|
config('configAuth', args.configAuth);
|
||||||
config('keysPanel', args['keys-panel']);
|
config('configPort', args.configPort);
|
||||||
|
config('keysPanel', args.keysPanel);
|
||||||
|
config('export', args.export);
|
||||||
|
config('exportToken', args.exportToken);
|
||||||
|
config('import', args.import);
|
||||||
|
config('importToken', args.importToken);
|
||||||
|
config('importListen', args.importListen);
|
||||||
|
config('importUrl', args.importUrl);
|
||||||
|
|
||||||
if (args['one-panel-mode']) {
|
config('dropbox', args.dropbox);
|
||||||
util.deprecate(noop, `cloudcmd --one-panel-mode: deprecated, use --one-file-panel instead`, 'DP0001')();
|
config('dropboxToken', args.dropboxToken);
|
||||||
config('oneFilePanel', true);
|
|
||||||
config('onePanelMode', true);
|
|
||||||
} else if (typeof args['one-panel-mode'] === 'boolean') {
|
|
||||||
util.deprecate(noop, `cloudcmd --no-one-panel-mode: deprecated, use --no-one-file-panel instead`, 'DP0001')();
|
|
||||||
config('oneFilePanel', false);
|
|
||||||
config('onePanelMode', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env('one_panel_mode')) {
|
await readConfig(args.config);
|
||||||
util.deprecate(noop, `CLOUDCMD_ONE_PANEL_MODE deprecated, use CLOUDCMD_ONE_FILE_PANEL instead`, 'DP0001')();
|
|
||||||
config('oneFilePanel', env.bool('one_panel_mode'));
|
|
||||||
config('onePanelMode', env.bool('one_panel_mode'));
|
|
||||||
}
|
|
||||||
|
|
||||||
readConfig(args.config);
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
root: args.root || '/', /* --no-root */
|
root: config('root'),
|
||||||
editor: args.editor,
|
editor: config('editor'),
|
||||||
packer: args.packer,
|
packer: config('packer'),
|
||||||
prefix: args.prefix || '', /* --no-prefix */
|
prefix: config('prefix'),
|
||||||
columns: args.columns,
|
prefixSocket: config('prefixSocket'),
|
||||||
|
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);
|
validateRoot(options.root, config);
|
||||||
|
|
||||||
if (args['show-config'])
|
if (args.showConfig)
|
||||||
showConfig();
|
await showConfig();
|
||||||
|
|
||||||
if (!args.save)
|
const {distributeImport} = await import('../server/distribute/import.js');
|
||||||
return start(options);
|
const importConfig = promisify(distributeImport);
|
||||||
|
|
||||||
config.save(() => {
|
await start(options, config);
|
||||||
start(options);
|
|
||||||
});
|
if (args.save)
|
||||||
|
config.write();
|
||||||
|
|
||||||
|
await tryToCatch(checkUpdate);
|
||||||
|
await importConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRoot(root) {
|
function validateRoot(root, config) {
|
||||||
const validate = require(DIR_SERVER + 'validate');
|
validate.root(root, config);
|
||||||
validate.root(root, console.log);
|
|
||||||
|
if (root === '/')
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (config('log'))
|
||||||
|
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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,11 +277,12 @@ function version() {
|
||||||
console.log('v' + Info.version);
|
console.log('v' + Info.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(config) {
|
async function start(options, config) {
|
||||||
const SERVER = DIR_SERVER + 'server';
|
if (!args.server)
|
||||||
|
return;
|
||||||
|
|
||||||
if (args.server)
|
const {default: server} = await import('../server/server.js');
|
||||||
require(SERVER)(config);
|
server(options, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function port(arg) {
|
function port(arg) {
|
||||||
|
|
@ -218,40 +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 result = tryCatch(readjsonSync, name);
|
|
||||||
const error = result[0];
|
|
||||||
const data = result[1];
|
|
||||||
|
|
||||||
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/legacy');
|
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));
|
||||||
|
|
@ -262,36 +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');
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkUpdate() {
|
async function checkUpdate() {
|
||||||
const load = require('package-json');
|
const {default: load} = await import('package-json');
|
||||||
const noop = () => {};
|
const {version} = await load(Info.name, 'latest');
|
||||||
|
|
||||||
load(Info.name, 'latest')
|
await showUpdateInfo(version);
|
||||||
.then(showUpdateInfo)
|
|
||||||
.catch(noop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUpdateInfo(data) {
|
async function showUpdateInfo(version) {
|
||||||
const version = data.version;
|
if (version === Info.version)
|
||||||
|
return;
|
||||||
|
|
||||||
if (version !== Info.version) {
|
const {default: chalk} = await import('chalk');
|
||||||
const chalk = require('chalk');
|
|
||||||
const rendy = require('rendy');
|
const latestVersion = chalk.green.bold(`v${version}`);
|
||||||
|
const latest = `update available: ${latestVersion}`;
|
||||||
const latest = rendy('update available: {{ latest }}', {
|
const current = chalk.dim(`(current: v${Info.version})`);
|
||||||
latest: chalk.green.bold('v' + version),
|
|
||||||
});
|
console.log('%s %s', latest, current);
|
||||||
|
|
||||||
const current = chalk.dim(rendy('(current: v{{ current }})', {
|
|
||||||
current: Info.version
|
|
||||||
}));
|
|
||||||
|
|
||||||
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) => {
|
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
||||||
|
|
||||||
|
const template = '- ' +
|
||||||
|
'*{{ date }}*, ' +
|
||||||
|
'**[v{{ version }}]' +
|
||||||
|
'(' +
|
||||||
|
link +
|
||||||
|
'v{{ version }})**\n';
|
||||||
|
|
||||||
|
const {version} = Info;
|
||||||
|
|
||||||
|
const [error, versionNew] = await tryToCatch(cl);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
console.error(error.message);
|
return console.error(error);
|
||||||
});
|
|
||||||
|
|
||||||
function main(callback) {
|
|
||||||
const history = 'Version history\n---------------\n';
|
|
||||||
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
|
||||||
const template = '- *{{ date }}*, ' +
|
|
||||||
'**[v{{ version }}]' +
|
|
||||||
'(' + link + 'v{{ version }})**\n';
|
|
||||||
|
|
||||||
const version = Info.version;
|
await replaceVersion('README.md', version, versionNew);
|
||||||
|
await replaceVersion('HELP.md', version, versionNew);
|
||||||
|
|
||||||
cl((error, versionNew) => {
|
const historyNew = history +
|
||||||
if (error)
|
rendy(template, {
|
||||||
return callback(error);
|
date: shortdate(),
|
||||||
|
version: versionNew,
|
||||||
replaceVersion('README.md', version, versionNew, callback);
|
|
||||||
replaceVersion('HELP.md', version, versionNew, function() {
|
|
||||||
const historyNew = history + rendy(template, {
|
|
||||||
date : shortdate(),
|
|
||||||
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)
|
|
||||||
return callback(error);
|
if (error)
|
||||||
|
return console.error(error);
|
||||||
callback(null, 'done: ' + name);
|
|
||||||
});
|
console.log(`done: ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cl(callback) {
|
async function cl() {
|
||||||
const argv = process.argv;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
36
bower.json
36
bower.json
|
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"name": "cloudcmd",
|
|
||||||
"homepage": "https://github.com/coderaiser/cloudcmd",
|
|
||||||
"authors": [
|
|
||||||
"coderaiser <mnemonic.enemy@gmail.com>"
|
|
||||||
],
|
|
||||||
"description": "orthodox web file manager with console and editor",
|
|
||||||
"main": "bin/cloudcmd.js",
|
|
||||||
"keywords": [
|
|
||||||
"console",
|
|
||||||
"editor",
|
|
||||||
"file",
|
|
||||||
"file manager",
|
|
||||||
"folder",
|
|
||||||
"orthodox",
|
|
||||||
"view",
|
|
||||||
"copy",
|
|
||||||
"rename",
|
|
||||||
"move",
|
|
||||||
"delete",
|
|
||||||
"file operation"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"private": true,
|
|
||||||
"ignore": [
|
|
||||||
"node_modules",
|
|
||||||
"modules",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"domtokenlist-shim": "~1.1.0",
|
|
||||||
"olark": "^1.0.0",
|
|
||||||
"jquery": "3.3.1",
|
|
||||||
"fancybox": "^2.1.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
663
client/client.js
663
client/client.js
|
|
@ -1,64 +1,59 @@
|
||||||
'use strict';
|
import process from 'node:process';
|
||||||
|
import Emitify from 'emitify';
|
||||||
/* global Util, DOM */
|
import inherits from 'inherits';
|
||||||
|
import {rendy} from 'rendy';
|
||||||
const itype = require('itype/legacy');
|
import load from 'load.js';
|
||||||
const Emitify = require('emitify/legacy');
|
import {tryToCatch} from 'try-to-catch';
|
||||||
const inherits = require('inherits');
|
import {addSlashToEnd} from 'format-io';
|
||||||
const rendy = require('rendy/legacy');
|
import pascalCase from 'just-pascal-case';
|
||||||
const exec = require('execon');
|
import currify from 'currify';
|
||||||
const Images = require('./dom/images');
|
import {
|
||||||
const join = require('join-io/www/join');
|
|
||||||
const jonny = require('jonny/legacy');
|
|
||||||
const currify = require('currify/legacy');
|
|
||||||
|
|
||||||
const bind = (f, ...a) => () => f(...a);
|
|
||||||
const noop = () => {};
|
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
/* global Util, DOM */
|
const noJS = (a) => a.replace(/.js$/, '');
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
inherits(CloudCmdProto, Emitify);
|
inherits(CloudCmdProto, Emitify);
|
||||||
|
|
||||||
module.exports = new CloudCmdProto(Util, DOM);
|
export const createCloudCmd = ({DOM, Listeners}) => {
|
||||||
|
return new CloudCmdProto({
|
||||||
|
DOM,
|
||||||
|
Listeners,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function CloudCmdProto(Util, DOM) {
|
load.addErrorListener((e, src) => {
|
||||||
let Key;
|
const msg = `file ${src} could not be loaded`;
|
||||||
let Debug;
|
Images.show.error(msg);
|
||||||
let Listeners;
|
});
|
||||||
|
|
||||||
const log = (str) => {
|
function CloudCmdProto({DOM, Listeners}) {
|
||||||
if (!Debug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
console.log(str);
|
|
||||||
};
|
|
||||||
|
|
||||||
Emitify.call(this);
|
Emitify.call(this);
|
||||||
|
|
||||||
const CloudCmd = this;
|
const CloudCmd = this;
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
const Storage = DOM.Storage;
|
|
||||||
const Files = DOM.Files;
|
|
||||||
|
|
||||||
this.log = log;
|
const {Storage, Files} = DOM;
|
||||||
this.PREFIX = '';
|
|
||||||
this.PREFIX_URL = '';
|
|
||||||
this.DIRCLIENT = '/dist/';
|
|
||||||
this.DIRCLIENT_MODULES = this.DIRCLIENT + 'modules/';
|
|
||||||
|
|
||||||
this.MIN_ONE_PANEL_WIDTH = 1155;
|
this.log = () => {
|
||||||
this.HOST = location.origin ||
|
if (!isDev)
|
||||||
location.protocol + '//' + location.host;
|
return;
|
||||||
|
};
|
||||||
const TITLE = 'Cloud Commander';
|
this.prefix = '';
|
||||||
this.TITLE = TITLE;
|
this.prefixSocket = '';
|
||||||
|
this.prefixURL = '';
|
||||||
|
|
||||||
|
this.MIN_ONE_PANEL_WIDTH = DOM.getCSSVar('min-one-panel-width');
|
||||||
|
this.HOST = location.origin || location.protocol + '//' + location.host;
|
||||||
this.sort = {
|
this.sort = {
|
||||||
left: 'name',
|
left: 'name',
|
||||||
right: 'name',
|
right: 'name',
|
||||||
|
|
@ -69,143 +64,51 @@ function CloudCmdProto(Util, DOM) {
|
||||||
right: 'asc',
|
right: 'asc',
|
||||||
};
|
};
|
||||||
|
|
||||||
log.enable = () => {
|
this.changeDir = async (path, overrides = {}) => {
|
||||||
Debug = true;
|
const {
|
||||||
};
|
isRefresh,
|
||||||
|
panel,
|
||||||
log.disable = () => {
|
history = true,
|
||||||
Debug = false;
|
noCurrent,
|
||||||
};
|
currentName,
|
||||||
|
} = overrides;
|
||||||
const kebabToCamelCase = Util.kebabToCamelCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Функция привязываеться ко всем ссылкам и
|
|
||||||
* загружает содержимое каталогов
|
|
||||||
*
|
|
||||||
* @param params - {
|
|
||||||
* paramLink - ссылка
|
|
||||||
* needRefresh - необходимость обязательной загрузки данных с сервера
|
|
||||||
* panel
|
|
||||||
* }
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
this.loadDir = (params, callback) => {
|
|
||||||
const p = params;
|
|
||||||
|
|
||||||
const refresh = p.isRefresh;
|
|
||||||
const panel = p.panel;
|
|
||||||
const history = p.history;
|
|
||||||
const noCurrent = p.noCurrent;
|
|
||||||
const currentName = p.currentName;
|
|
||||||
|
|
||||||
|
const refresh = isRefresh;
|
||||||
let panelChanged;
|
let panelChanged;
|
||||||
if (!noCurrent)
|
|
||||||
if (panel && panel !== Info.panel) {
|
if (!noCurrent && panel && panel !== Info.panel) {
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
panelChanged = true;
|
panelChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let imgPosition;
|
let imgPosition;
|
||||||
|
|
||||||
if (panelChanged || refresh || !history)
|
if (panelChanged || refresh || !history)
|
||||||
imgPosition = 'top';
|
imgPosition = 'top';
|
||||||
|
|
||||||
Images.show.load(imgPosition, panel);
|
Images.show.load(imgPosition, panel);
|
||||||
|
|
||||||
/* загружаем содержимое каталога */
|
/* загружаем содержимое каталога */
|
||||||
ajaxLoad(p.path, {
|
await ajaxLoad(addSlashToEnd(path), {
|
||||||
refresh,
|
refresh,
|
||||||
history,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
}, panel, callback);
|
showDotFiles: CloudCmd.config('showDotFiles'),
|
||||||
|
}, panel);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* function load modules
|
|
||||||
* @params = {name, path, func, dobefore, arg}
|
|
||||||
*/
|
|
||||||
function loadModule(params) {
|
|
||||||
if (!params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let path = params.path;
|
|
||||||
const name = params.name || path && kebabToCamelCase(path);
|
|
||||||
const func = params.func;
|
|
||||||
const funcName = params.funcName;
|
|
||||||
const doBefore = params.dobefore;
|
|
||||||
|
|
||||||
const isContain = /\.js/.test(path);
|
|
||||||
|
|
||||||
if (!isContain)
|
|
||||||
path += '.js';
|
|
||||||
|
|
||||||
if (CloudCmd[name])
|
|
||||||
return;
|
|
||||||
|
|
||||||
CloudCmd[name] = (...args) => {
|
|
||||||
const prefix = CloudCmd.PREFIX;
|
|
||||||
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path;
|
|
||||||
|
|
||||||
exec(doBefore);
|
|
||||||
|
|
||||||
const done = (error) => {
|
|
||||||
const Proto = CloudCmd[name];
|
|
||||||
|
|
||||||
if (error || !itype.function(Proto))
|
|
||||||
return;
|
|
||||||
|
|
||||||
CloudCmd[name] = applyConstructor(Proto, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
return DOM.load.js(pathFull, func || done);
|
|
||||||
};
|
|
||||||
|
|
||||||
CloudCmd[name][funcName] = CloudCmd[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* apply arguemnts to constructor
|
|
||||||
*
|
|
||||||
* @param constructor
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
function applyConstructor(constructor, args) {
|
|
||||||
const F = function () {
|
|
||||||
return constructor.apply(this, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
F.prototype = constructor.prototype;
|
|
||||||
return new F();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Конструктор CloudClient, который
|
* Конструктор CloudClient, который
|
||||||
* выполняет весь функционал по
|
* выполняет весь функционал по
|
||||||
* инициализации
|
* инициализации
|
||||||
*/
|
*/
|
||||||
this.init = (prefix, config) => {
|
this.init = async (prefix, config) => {
|
||||||
const func = bind(exec.parallel, [
|
CloudCmd.prefix = prefix;
|
||||||
initModules,
|
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
||||||
baseInit,
|
CloudCmd.prefixSocket = config.prefixSocket;
|
||||||
loadPlugins,
|
CloudCmd.DIR_DIST = `${prefix}/dist`;
|
||||||
loadStyle,
|
CloudCmd.DIR_MODULES = `${this.DIR_DIST}/modules`;
|
||||||
exec.with(CloudCmd.route, location.hash),
|
|
||||||
], noop);
|
|
||||||
|
|
||||||
const funcBefore = (callback) => {
|
|
||||||
const src = prefix + '/join:' + [
|
|
||||||
CloudCmd.DIRCLIENT_MODULES + 'polyfill.js',
|
|
||||||
'/modules/domtokenlist-shim/dist/domtokenlist.min.js',
|
|
||||||
].join(':');
|
|
||||||
|
|
||||||
DOM.loadJquery(() => {
|
|
||||||
DOM.load.js(src, callback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
CloudCmd.PREFIX = prefix;
|
|
||||||
CloudCmd.PREFIX_URL = prefix + apiURL;
|
|
||||||
|
|
||||||
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));
|
||||||
|
|
@ -214,46 +117,22 @@ function CloudCmdProto(Util, 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;
|
||||||
|
|
||||||
config[key] = value;
|
config[key] = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
DOM.Storage.setAllowed(CloudCmd.config('localStorage'));
|
if (config.oneFilePanel)
|
||||||
|
|
||||||
if (config.onePanelMode)
|
|
||||||
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
||||||
|
|
||||||
exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
|
if (!document.body.scrollIntoViewIfNeeded)
|
||||||
};
|
await load.js(`${CloudCmd.DIR_MODULES}/polyfill.js`);
|
||||||
|
|
||||||
function loadStyle(callback) {
|
|
||||||
const prefix = CloudCmd.PREFIX;
|
|
||||||
const plugins = prefix + '/dist/cloudcmd.common.css';
|
|
||||||
|
|
||||||
DOM.load.css(plugins, callback);
|
await initModules();
|
||||||
}
|
await baseInit();
|
||||||
|
|
||||||
function loadPlugins(callback) {
|
|
||||||
const prefix = CloudCmd.PREFIX;
|
|
||||||
const plugins = prefix + '/plugins.js';
|
|
||||||
|
|
||||||
DOM.load.js(plugins, callback);
|
CloudCmd.route(location.hash);
|
||||||
}
|
|
||||||
|
|
||||||
this.join = (urls) => {
|
|
||||||
const prefix = CloudCmd.PREFIX;
|
|
||||||
|
|
||||||
if (!Array.isArray(urls))
|
|
||||||
throw Error('urls should be array!');
|
|
||||||
|
|
||||||
const noPrefixUrls = urls.map((url) => {
|
|
||||||
return url.replace(prefix, '');
|
|
||||||
});
|
|
||||||
|
|
||||||
return prefix + join(noPrefixUrls);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.route = (path) => {
|
this.route = (path) => {
|
||||||
|
|
@ -263,91 +142,87 @@ function CloudCmdProto(Util, DOM) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const [kebabModule] = query;
|
const [kebabModule] = query;
|
||||||
const module = kebabToCamelCase(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
DOM.setCurrentFile(current);
|
DOM.setCurrentFile(current);
|
||||||
|
|
||||||
CloudCmd.execFromModule(module, 'show');
|
CloudCmd.execFromModule(module, 'show');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.logOut = () => {
|
this.logOut = async () => {
|
||||||
const url = CloudCmd.PREFIX + '/logout';
|
const url = CloudCmd.prefix + '/logout';
|
||||||
const error = () => document.location.reload();
|
const error = () => document.location.reload();
|
||||||
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
DOM.load.ajax({url, error});
|
await DOM.Storage.clear();
|
||||||
|
unregisterSW(prefix);
|
||||||
|
DOM.load.ajax({
|
||||||
|
url,
|
||||||
|
error,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function initModules(callback) {
|
const initModules = async () => {
|
||||||
exec.if(CloudCmd.Key, () => {
|
CloudCmd.Key = Key;
|
||||||
Key = new CloudCmd.Key();
|
CloudCmd.Key.bind();
|
||||||
CloudCmd.Key = Key;
|
|
||||||
Key.bind();
|
|
||||||
}, (func) => {
|
|
||||||
/* привязываем клавиши к функциям */
|
|
||||||
const path = 'key.js';
|
|
||||||
|
|
||||||
loadModule({
|
|
||||||
path,
|
|
||||||
func
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Files.get('modules', (error, modules) => {
|
const [, modules] = await tryToCatch(Files.get, 'modules');
|
||||||
const showLoad = Images.show.load;
|
const showLoad = Images.show.load;
|
||||||
|
|
||||||
const doBefore = {
|
const doBefore = {
|
||||||
'edit': showLoad,
|
edit: showLoad,
|
||||||
'menu': showLoad,
|
menu: showLoad,
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = (name, path, dobefore) => {
|
const load = (name, path, dobefore) => {
|
||||||
const isTmpl = path === 'template';
|
loadModule({
|
||||||
const funcName = isTmpl ? 'get' : 'show';
|
name,
|
||||||
|
path,
|
||||||
loadModule({
|
dobefore,
|
||||||
name,
|
|
||||||
path,
|
|
||||||
dobefore,
|
|
||||||
funcName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!modules)
|
|
||||||
modules = [];
|
|
||||||
|
|
||||||
modules.local.forEach((module) => {
|
|
||||||
load(null, module, doBefore[module]);
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
callback();
|
|
||||||
});
|
if (!modules)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const module of modules.local) {
|
||||||
|
load(null, module, doBefore[module]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function saveCurrentName(currentName) {
|
||||||
|
await Storage.set('current-name', currentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function baseInit(callback) {
|
async function baseInit() {
|
||||||
const files = DOM.getFiles();
|
const files = DOM.getFiles();
|
||||||
|
|
||||||
CloudCmd.on('current-file', DOM.updateCurrentInfo);
|
CloudCmd.on('current-file', DOM.updateCurrentInfo);
|
||||||
|
CloudCmd.on('current-name', saveCurrentName);
|
||||||
|
|
||||||
|
const name = await Storage.get('current-name');
|
||||||
|
const currentFile = name && DOM.getCurrentByName(name) || files[0];
|
||||||
|
|
||||||
/* выделяем строку с первым файлом */
|
/* выделяем строку с первым файлом */
|
||||||
if (files)
|
if (files)
|
||||||
DOM.setCurrentFile(files[0], {
|
DOM.setCurrentFile(currentFile, {
|
||||||
// when hash is present
|
// when hash is present
|
||||||
// it should be handled with this.route
|
// it should be handled with this.route
|
||||||
// overwre otherwise
|
// overwre otherwise
|
||||||
history: !location.hash
|
history: !location.hash,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dirPath = DOM.getCurrentDirPath();
|
const dirPath = DOM.getCurrentDirPath();
|
||||||
Listeners = CloudCmd.Listeners;
|
|
||||||
Listeners.init();
|
Listeners.init();
|
||||||
|
|
||||||
const panels = getPanels();
|
const panels = getPanels();
|
||||||
|
|
@ -356,21 +231,18 @@ function CloudCmdProto(Util, DOM) {
|
||||||
Listeners.initKeysPanel();
|
Listeners.initKeysPanel();
|
||||||
|
|
||||||
if (!CloudCmd.config('dirStorage'))
|
if (!CloudCmd.config('dirStorage'))
|
||||||
return callback();
|
return;
|
||||||
|
|
||||||
Storage.get(dirPath, (error, data) => {
|
const data = await Storage.get(dirPath);
|
||||||
if (!data) {
|
|
||||||
data = getJSONfromFileTable();
|
if (!data)
|
||||||
Storage.set(dirPath, data);
|
await Storage.setJson(dirPath, getJsonFromFileTable());
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanels() {
|
function getPanels() {
|
||||||
const panels = ['left'];
|
const panels = ['left'];
|
||||||
|
|
||||||
if (CloudCmd.config('onePanelMode'))
|
if (CloudCmd.config('oneFilePanel'))
|
||||||
return panels;
|
return panels;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -379,40 +251,29 @@ function CloudCmdProto(Util, DOM) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.execFromModule = (moduleName, funcName, ...args) => {
|
this.execFromModule = async (moduleName, funcName, ...args) => {
|
||||||
const obj = CloudCmd[moduleName];
|
await CloudCmd[moduleName]();
|
||||||
const isObj = itype.object(obj);
|
|
||||||
|
|
||||||
exec.if(isObj, () => {
|
const func = CloudCmd[moduleName][funcName];
|
||||||
const obj = CloudCmd[moduleName];
|
func(...args);
|
||||||
const func = obj[funcName];
|
|
||||||
|
|
||||||
func(...args);
|
|
||||||
}, obj);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.refresh = (options = {}, callback) => {
|
this.refresh = async (options = {}) => {
|
||||||
if (!callback && typeof options === 'function') {
|
const {panel = Info.panel, currentName} = options;
|
||||||
callback = options;
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const panel = options.panel || Info.panel;
|
|
||||||
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;
|
||||||
const currentName = options.currentName;
|
|
||||||
|
|
||||||
CloudCmd.loadDir({
|
await CloudCmd.changeDir(path, {
|
||||||
path,
|
|
||||||
isRefresh,
|
isRefresh,
|
||||||
history,
|
history,
|
||||||
panel,
|
panel,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
}, callback);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -422,176 +283,132 @@ function CloudCmdProto(Util, DOM) {
|
||||||
* @param options
|
* @param options
|
||||||
* { refresh, history } - необходимость обновить данные о каталоге
|
* { refresh, history } - необходимость обновить данные о каталоге
|
||||||
* @param panel
|
* @param panel
|
||||||
* @param callback
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function ajaxLoad(path, options, panel, callback) {
|
async function ajaxLoad(path, options = {}, panel) {
|
||||||
const create = (error, json) => {
|
const {RESTful} = DOM;
|
||||||
const RESTful = DOM.RESTful;
|
|
||||||
const name = options.currentName || Info.name;
|
|
||||||
const obj = jonny.parse(json);
|
|
||||||
const isRefresh = options.refresh;
|
|
||||||
const noCurrent = options.noCurrent;
|
|
||||||
|
|
||||||
if (!isRefresh && json)
|
|
||||||
return createFileTable(obj, panel, options, callback);
|
|
||||||
|
|
||||||
const position = DOM.getPanelPosition(panel);
|
|
||||||
const sort = CloudCmd.sort[position];
|
|
||||||
const order = CloudCmd.order[position];
|
|
||||||
|
|
||||||
const query = rendy('?sort={{ sort }}&order={{ order }}', {
|
|
||||||
sort,
|
|
||||||
order,
|
|
||||||
});
|
|
||||||
|
|
||||||
RESTful.read(path + query, 'json', (error, obj) => {
|
|
||||||
if (error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
options.sort = sort;
|
|
||||||
options.order = order;
|
|
||||||
|
|
||||||
createFileTable(obj, panel, options, () => {
|
|
||||||
if (isRefresh && !noCurrent)
|
|
||||||
DOM.setCurrentByName(name);
|
|
||||||
|
|
||||||
exec(callback);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!CloudCmd.config('dirStorage'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Storage.set(path, obj);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!options)
|
CloudCmd.log(`reading dir: "${path}";`);
|
||||||
options = {};
|
|
||||||
|
|
||||||
CloudCmd.log('reading dir: "' + path + '";');
|
const dirStorage = CloudCmd.config('dirStorage');
|
||||||
|
const json = dirStorage && await Storage.getJson(path);
|
||||||
|
|
||||||
|
const name = options.currentName || Info.name;
|
||||||
|
const {noCurrent, refresh} = options;
|
||||||
|
|
||||||
|
if (!refresh && json)
|
||||||
|
return await createFileTable(json, panel, options);
|
||||||
|
|
||||||
|
const position = DOM.getPanelPosition(panel);
|
||||||
|
const sort = CloudCmd.sort[position];
|
||||||
|
const order = CloudCmd.order[position];
|
||||||
|
|
||||||
|
const query = rendy('?sort={{ sort }}&order={{ order }}', {
|
||||||
|
sort,
|
||||||
|
order,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [, newObj] = await RESTful.read(path + query, 'json');
|
||||||
|
|
||||||
|
if (!newObj)
|
||||||
|
// that's OK, error handled by RESTful
|
||||||
|
return;
|
||||||
|
|
||||||
|
options.sort = sort;
|
||||||
|
options.order = order;
|
||||||
|
|
||||||
|
await createFileTable(newObj, panel, options);
|
||||||
|
|
||||||
|
if (refresh && !noCurrent)
|
||||||
|
DOM.setCurrentByName(name);
|
||||||
|
|
||||||
if (!CloudCmd.config('dirStorage'))
|
if (!CloudCmd.config('dirStorage'))
|
||||||
return create();
|
return;
|
||||||
|
|
||||||
Storage.get(path, create);
|
Storage.setJson(path, newObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция строит файловую таблицу
|
* Функция строит файловую таблицу
|
||||||
* @param json - данные о файлах
|
* @param data - данные о файлах
|
||||||
* @param panelParam
|
* @param panelParam
|
||||||
* @param history
|
* @param options - history, noCurrent, showDotFiles
|
||||||
* @param callback
|
|
||||||
*/
|
*/
|
||||||
function createFileTable(json, panelParam, options, callback) {
|
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',
|
||||||
|
];
|
||||||
|
|
||||||
Files.get(names, (error, templFile, templPath, templLink, templPathLink) => {
|
const [error, [
|
||||||
const Dialog = DOM.Dialog;
|
file,
|
||||||
const panel = panelParam || DOM.getPanel();
|
path,
|
||||||
|
link,
|
||||||
|
pathLink,
|
||||||
|
]] = await tryToCatch(Files.get, names);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return DOM.Dialog.alert(error.responseText);
|
||||||
|
|
||||||
|
const panel = panelParam || DOM.getPanel();
|
||||||
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
|
const {dir, name} = Info;
|
||||||
|
|
||||||
|
const {childNodes} = panel;
|
||||||
|
let i = childNodes.length;
|
||||||
|
|
||||||
|
while (i--)
|
||||||
|
panel.removeChild(panel.lastChild);
|
||||||
|
|
||||||
|
panel.innerHTML = buildFromJSON({
|
||||||
|
sort: options.sort,
|
||||||
|
order: options.order,
|
||||||
|
data,
|
||||||
|
id: panel.id,
|
||||||
|
prefix,
|
||||||
|
showDotFiles,
|
||||||
|
template: {
|
||||||
|
file,
|
||||||
|
path,
|
||||||
|
pathLink,
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Listeners.setOnPanel(panel);
|
||||||
|
|
||||||
|
if (!noCurrent) {
|
||||||
|
let current;
|
||||||
|
|
||||||
const {
|
if (name === '..' && dir !== '/')
|
||||||
dir,
|
current = DOM.getCurrentByName(dir);
|
||||||
name,
|
|
||||||
} = Info;
|
|
||||||
|
|
||||||
if (error)
|
if (!current)
|
||||||
return Dialog.alert(TITLE, error.responseText);
|
[current] = DOM.getFiles(panel);
|
||||||
|
|
||||||
const childNodes = panel.childNodes;
|
DOM.setCurrentFile(current, {
|
||||||
let i = childNodes.length;
|
history,
|
||||||
|
|
||||||
while (i--)
|
|
||||||
panel.removeChild(panel.lastChild);
|
|
||||||
|
|
||||||
panel.innerHTML = buildFromJSON({
|
|
||||||
sort : options.sort,
|
|
||||||
order : options.order,
|
|
||||||
data : json,
|
|
||||||
id : panel.id,
|
|
||||||
prefix : CloudCmd.PREFIX,
|
|
||||||
template : {
|
|
||||||
file : templFile,
|
|
||||||
path : templPath,
|
|
||||||
pathLink : templPathLink,
|
|
||||||
link : templLink
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Listeners.setOnPanel(panel);
|
CloudCmd.emit('active-dir', Info.dirPath);
|
||||||
|
}
|
||||||
if (!noCurrent) {
|
|
||||||
let current;
|
|
||||||
|
|
||||||
if (name === '..' && dir !== '/')
|
|
||||||
current = DOM.getCurrentByName(dir);
|
|
||||||
|
|
||||||
if (!current)
|
|
||||||
current = DOM.getFiles(panel)[0];
|
|
||||||
|
|
||||||
DOM.setCurrentFile(current, {
|
|
||||||
history: history
|
|
||||||
});
|
|
||||||
|
|
||||||
CloudCmd.emit('active-dir', Info.dirPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
exec(callback);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
this.goToParentDir = async () => {
|
||||||
* Функция генерирует JSON из html-таблицы файлов и
|
|
||||||
* используеться при первом заходе в корень
|
|
||||||
*/
|
|
||||||
function getJSONfromFileTable() {
|
|
||||||
const path = DOM.getCurrentDirPath();
|
|
||||||
const infoFiles = Info.files || [];
|
|
||||||
|
|
||||||
const notParent = (current) => {
|
|
||||||
const name = DOM.getCurrentName(current);
|
|
||||||
return name !== '..';
|
|
||||||
};
|
|
||||||
|
|
||||||
const parse = (current) => {
|
|
||||||
const name = DOM.getCurrentName(current);
|
|
||||||
const size = DOM.getCurrentSize(current);
|
|
||||||
const owner = DOM.getCurrentOwner(current);
|
|
||||||
const mode = DOM.getCurrentMode(current);
|
|
||||||
const date = DOM.getCurrentDate(current);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
size,
|
|
||||||
mode,
|
|
||||||
owner,
|
|
||||||
date,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const files = infoFiles
|
|
||||||
.filter(notParent)
|
|
||||||
.map(parse);
|
|
||||||
|
|
||||||
const fileTable = {
|
|
||||||
path,
|
|
||||||
files,
|
|
||||||
};
|
|
||||||
|
|
||||||
return fileTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.goToParentDir = () => {
|
|
||||||
const dir = Info.dir;
|
|
||||||
const {
|
const {
|
||||||
|
dir,
|
||||||
dirPath,
|
dirPath,
|
||||||
parentDirPath,
|
parentDirPath,
|
||||||
|
panel,
|
||||||
} = Info;
|
} = Info;
|
||||||
|
|
||||||
if (dirPath === parentDirPath)
|
if (dirPath === parentDirPath)
|
||||||
|
|
@ -599,15 +416,13 @@ function CloudCmdProto(Util, DOM) {
|
||||||
|
|
||||||
const path = parentDirPath;
|
const path = parentDirPath;
|
||||||
|
|
||||||
CloudCmd.loadDir({path}, () => {
|
await CloudCmd.changeDir(path);
|
||||||
const {panel} = Info;
|
|
||||||
const current = DOM.getCurrentByName(dir);
|
const current = DOM.getCurrentByName(dir);
|
||||||
const first = DOM.getFiles(panel)[0];
|
const [first] = DOM.getFiles(panel);
|
||||||
|
|
||||||
DOM.setCurrentFile(current || first, {
|
DOM.setCurrentFile(current || first, {
|
||||||
history
|
history,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,74 @@
|
||||||
'use strict';
|
import '../css/main.css';
|
||||||
|
import process from 'node:process';
|
||||||
|
import wraptile from 'wraptile';
|
||||||
|
import load from 'load.js';
|
||||||
|
import * as Util from '#common/util';
|
||||||
|
import * as CloudFunc from '#common/cloudfunc';
|
||||||
|
import DOM from '#dom';
|
||||||
|
import {registerSW, listenSW} from './sw/register.js';
|
||||||
|
import {initSortPanel, sortPanel} from './sort.js';
|
||||||
|
import {createCloudCmd} from './client.js';
|
||||||
|
import * as Listeners from './listeners/index.js';
|
||||||
|
|
||||||
require('../css/main.css');
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
require('../css/nojs.css');
|
|
||||||
require('../css/columns/name-size-date.css');
|
|
||||||
require('../css/columns/name-size.css');
|
|
||||||
|
|
||||||
// prevent additional loading of emitify
|
export default init;
|
||||||
window.Emitify = require('emitify/legacy');
|
|
||||||
|
|
||||||
module.exports = window.CloudCmd = (config) => {
|
globalThis.CloudCmd = init;
|
||||||
window.Promise = window.Promise || require('es6-promise');
|
|
||||||
window.Util = require('../common/util');
|
async function init(config) {
|
||||||
window.CloudFunc = require('../common/cloudfunc');
|
globalThis.CloudCmd = createCloudCmd({
|
||||||
|
DOM,
|
||||||
const DOM = require('./dom');
|
Listeners,
|
||||||
|
|
||||||
window.DOM = DOM;
|
|
||||||
window.CloudCmd = require('./client');
|
|
||||||
|
|
||||||
const Dialog = require('./dom/dialog');
|
|
||||||
|
|
||||||
const prefix = getPrefix(config.prefix);
|
|
||||||
const {htmlDialogs} = config;
|
|
||||||
|
|
||||||
DOM.Dialog = Dialog(prefix, {
|
|
||||||
htmlDialogs
|
|
||||||
});
|
});
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.Util = Util;
|
||||||
|
globalThis.CloudFunc = CloudFunc;
|
||||||
|
|
||||||
require('./listeners');
|
await register(config);
|
||||||
require('./key');
|
|
||||||
require('./sort');
|
|
||||||
|
|
||||||
window.CloudCmd.init(prefix, config);
|
initSortPanel();
|
||||||
};
|
globalThis.CloudCmd.sortPanel = sortPanel;
|
||||||
|
const prefix = getPrefix(config.prefix);
|
||||||
|
|
||||||
|
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)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
if (!prefix.indexOf('/'))
|
if (!prefix.indexOf('/'))
|
||||||
return prefix;
|
return prefix;
|
||||||
|
|
||||||
return '/' + prefix;
|
return `/${prefix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onUpdateFound = wraptile(async (config) => {
|
||||||
|
if (isDev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
const prefix = getPrefix(config.prefix);
|
||||||
|
|
||||||
|
await load.js(`${prefix}/dist/cloudcmd.common.js`);
|
||||||
|
await load.js(`${prefix}/dist/cloudcmd.js`);
|
||||||
|
|
||||||
|
console.log('cloudcmd: sw: updated');
|
||||||
|
|
||||||
|
DOM.Events.removeAll();
|
||||||
|
globalThis.CloudCmd(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function register(config) {
|
||||||
|
const {prefix} = config;
|
||||||
|
const sw = await registerSW(prefix);
|
||||||
|
|
||||||
|
listenSW(sw, 'updatefound', onUpdateFound(config));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,133 +1,124 @@
|
||||||
'use strict';
|
/* global CloudCmd*/
|
||||||
|
import * as Storage from '#dom/storage';
|
||||||
|
import tryToPromiseAll from '../../common/try-to-promise-all.js';
|
||||||
|
|
||||||
/* global CloudCmd */
|
const CLASS = 'cut-file';
|
||||||
|
const COPY = 'copy';
|
||||||
|
const CUT = 'cut';
|
||||||
|
|
||||||
const jonny = require('jonny/legacy');
|
function showMessage(msg) {
|
||||||
const exec = require('execon');
|
globalThis.DOM.Dialog.alert(msg);
|
||||||
|
|
||||||
const Storage = require('./storage');
|
|
||||||
const DOM = require('./');
|
|
||||||
|
|
||||||
module.exports = new BufferProto();
|
|
||||||
|
|
||||||
function BufferProto() {
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const CLASS = 'cut-file';
|
|
||||||
const COPY = 'copy';
|
|
||||||
const CUT = 'cut';
|
|
||||||
const TITLE = 'Buffer';
|
|
||||||
|
|
||||||
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) {
|
|
||||||
DOM.Dialog.alert(TITLE, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNames() {
|
|
||||||
const files = DOM.getActiveFiles();
|
|
||||||
const names = DOM.getFilenames(files);
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addCutClass() {
|
|
||||||
const files = DOM.getActiveFiles();
|
|
||||||
|
|
||||||
files.forEach((element) => {
|
|
||||||
element.classList.add(CLASS);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function rmCutClass() {
|
|
||||||
const files = DOM.getByClassAll(CLASS);
|
|
||||||
|
|
||||||
[...files].forEach((element) => {
|
|
||||||
element.classList.remove(CLASS);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function callIfEnabled(callback) {
|
|
||||||
const is = CloudCmd.config('buffer');
|
|
||||||
|
|
||||||
if (is)
|
|
||||||
return callback();
|
|
||||||
|
|
||||||
showMessage('Buffer disabled in config!');
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy() {
|
|
||||||
const names = getNames();
|
|
||||||
const from = Info.dirPath;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
if (!names.length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Storage.remove(CUT)
|
|
||||||
.set(COPY, {
|
|
||||||
from,
|
|
||||||
names,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function cut() {
|
|
||||||
const names = getNames();
|
|
||||||
const from = Info.dirPath;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
if (!names.length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
addCutClass();
|
|
||||||
|
|
||||||
Storage
|
|
||||||
.set(CUT, {
|
|
||||||
from,
|
|
||||||
names,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
Storage.remove(COPY)
|
|
||||||
.remove(CUT);
|
|
||||||
|
|
||||||
rmCutClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
function paste() {
|
|
||||||
const copy = Storage.get.bind(Storage, COPY);
|
|
||||||
const cut = Storage.get.bind(Storage, CUT);
|
|
||||||
|
|
||||||
exec.parallel([copy, cut], (error, cp, ct) => {
|
|
||||||
const opStr = cp ? 'copy' : 'move';
|
|
||||||
const opData = cp || ct;
|
|
||||||
const Operation = CloudCmd.Operation;
|
|
||||||
const msg = 'Path is same!';
|
|
||||||
const path = Info.dirPath;
|
|
||||||
|
|
||||||
if (!error && !cp && !ct)
|
|
||||||
error = 'Buffer is empty!';
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return showMessage(error);
|
|
||||||
|
|
||||||
const data = jonny.parse(opData);
|
|
||||||
data.to = path;
|
|
||||||
|
|
||||||
if (data.from === path)
|
|
||||||
return showMessage(msg);
|
|
||||||
|
|
||||||
Operation.show(opStr, data);
|
|
||||||
clear();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNames() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
const files = DOM.getActiveFiles();
|
||||||
|
|
||||||
|
return DOM.getFilenames(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCutClass() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
const files = DOM.getActiveFiles();
|
||||||
|
|
||||||
|
for (const element of files) {
|
||||||
|
element.classList.add(CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rmCutClass() {
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
const files = DOM.getByClassAll(CLASS);
|
||||||
|
|
||||||
|
for (const element of files) {
|
||||||
|
element.classList.remove(CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkEnabled = (fn) => () => {
|
||||||
|
const is = CloudCmd.config('buffer');
|
||||||
|
|
||||||
|
if (is)
|
||||||
|
return fn();
|
||||||
|
|
||||||
|
showMessage('Buffer disabled in config!');
|
||||||
|
};
|
||||||
|
|
||||||
|
async function readBuffer() {
|
||||||
|
const [e, cp, ct] = await tryToPromiseAll([
|
||||||
|
Storage.getJson(COPY),
|
||||||
|
Storage.getJson(CUT),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
e,
|
||||||
|
cp,
|
||||||
|
ct,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const copy = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
|
const names = getNames();
|
||||||
|
const from = Info.dirPath;
|
||||||
|
|
||||||
|
await clear();
|
||||||
|
|
||||||
|
if (!names.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Storage.remove(CUT);
|
||||||
|
await Storage.setJson(COPY, {
|
||||||
|
from,
|
||||||
|
names,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cut = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
|
const names = getNames();
|
||||||
|
const from = Info.dirPath;
|
||||||
|
|
||||||
|
await clear();
|
||||||
|
|
||||||
|
if (!names.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
addCutClass();
|
||||||
|
|
||||||
|
await Storage.setJson(CUT, {
|
||||||
|
from,
|
||||||
|
names,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const clear = checkEnabled(async () => {
|
||||||
|
await Storage.remove(COPY);
|
||||||
|
await Storage.remove(CUT);
|
||||||
|
|
||||||
|
rmCutClass();
|
||||||
|
});
|
||||||
|
|
||||||
|
export const paste = checkEnabled(async () => {
|
||||||
|
const Info = globalThis.DOM.CurrentInfo;
|
||||||
|
const [error, cp, ct] = await readBuffer();
|
||||||
|
|
||||||
|
if (error || !cp && !ct)
|
||||||
|
return showMessage(error || 'Buffer is empty!');
|
||||||
|
|
||||||
|
const opStr = cp ? 'copy' : 'move';
|
||||||
|
const data = cp || ct;
|
||||||
|
const {Operation} = CloudCmd;
|
||||||
|
const msg = 'Path is same!';
|
||||||
|
const to = Info.dirPath;
|
||||||
|
|
||||||
|
if (data.from === to)
|
||||||
|
return showMessage(msg);
|
||||||
|
|
||||||
|
Operation.show(opStr, {
|
||||||
|
...data,
|
||||||
|
to,
|
||||||
|
});
|
||||||
|
|
||||||
|
await clear();
|
||||||
|
});
|
||||||
|
|
|
||||||
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,25 +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';
|
||||||
|
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');
|
||||||
|
|
||||||
|
export const _CURRENT_FILE = CURRENT_FILE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set name from current (or param) file
|
* set name from current (or param) file
|
||||||
|
|
@ -27,18 +18,18 @@ const SPACE = ' ';
|
||||||
* @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;
|
||||||
const dir = PREFIX + FS + Info.dirPath;
|
const dir = prefix + FS + Info.dirPath;
|
||||||
const encoded = encode(name);
|
const encoded = encode(name);
|
||||||
|
|
||||||
link.title = encoded;
|
link.title = encoded;
|
||||||
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;
|
||||||
|
|
@ -49,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);
|
|
||||||
|
|
||||||
if (!link)
|
return parseNameAttribute(current.getAttribute('data-name'));
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,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)[0];
|
const [element] = DOM.getByTag('a', current);
|
||||||
const prefix = CloudCmd.PREFIX;
|
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);
|
||||||
|
|
@ -119,11 +125,11 @@ 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,
|
||||||
});
|
});
|
||||||
|
|
||||||
return DOM.getCurrentDirPath(panel);
|
return DOM.getCurrentDirPath(panel);
|
||||||
|
|
@ -134,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
|
||||||
*/
|
*/
|
||||||
|
|
@ -163,9 +169,8 @@ 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 CENTER = true;
|
|
||||||
const currentFileWas = DOM.getCurrentFile();
|
const currentFileWas = DOM.getCurrentFile();
|
||||||
|
|
||||||
if (!currentFile)
|
if (!currentFile)
|
||||||
|
|
@ -193,13 +198,15 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* scrolling to current file */
|
/* scrolling to current file */
|
||||||
|
const CENTER = true;
|
||||||
|
|
||||||
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
||||||
|
|
||||||
CloudCmd.emit('current-file', currentFile);
|
CloudCmd.emit('current-file', currentFile);
|
||||||
|
|
@ -209,13 +216,18 @@ module.exports.setCurrentFile = (currentFile, options) => {
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setCurrentByName = (name) => {
|
||||||
|
const current = DOM.getCurrentByName(name);
|
||||||
|
return DOM.setCurrentFile(current);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set current file by position
|
* set current file by position
|
||||||
*
|
*
|
||||||
* @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) => {
|
||||||
|
|
@ -247,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;
|
||||||
|
|
||||||
|
|
@ -259,18 +271,41 @@ 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] ||
|
Title = DOM.getByTag('title')[0] || createElement('title', {
|
||||||
DOM.load({
|
innerHTML: name,
|
||||||
name : 'title',
|
parent: document.head,
|
||||||
innerHTML : name,
|
});
|
||||||
parentElement : document.head
|
|
||||||
});
|
|
||||||
|
|
||||||
Title.textContent = name;
|
Title.textContent = name;
|
||||||
|
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check is current file is a directory
|
||||||
|
*
|
||||||
|
* @param currentFile
|
||||||
|
*/
|
||||||
|
export const isCurrentIsDir = (currentFile) => {
|
||||||
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
const path = DOM.getCurrentPath(current);
|
||||||
|
const fileType = DOM.getCurrentType(current);
|
||||||
|
|
||||||
|
const isZip = path.endsWith('.zip');
|
||||||
|
const isDir = /^directory(-link)?/.test(fileType);
|
||||||
|
|
||||||
|
return isDir || isZip;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurrentType = (currentFile) => {
|
||||||
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
const el = DOM.getByDataName('js-type', current);
|
||||||
|
|
||||||
|
const type = el.className
|
||||||
|
.split(' ')
|
||||||
|
.pop();
|
||||||
|
|
||||||
|
return type;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,128 +1,114 @@
|
||||||
'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('tape');
|
|
||||||
const diff = require('sinon-called-with-diff');
|
|
||||||
const sinon = diff(require('sinon'));
|
|
||||||
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;
|
||||||
|
|
||||||
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 setAttribute = sinon.stub();
|
const current = create();
|
||||||
const current = {
|
const {setAttribute} = current;
|
||||||
setAttribute
|
|
||||||
};
|
|
||||||
|
|
||||||
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 setAttribute = sinon.stub();
|
const current = create();
|
||||||
const current = {
|
const {setAttribute} = current;
|
||||||
setAttribute
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = sinon.stub();
|
const emit = stub();
|
||||||
const setAttribute = sinon.stub();
|
|
||||||
|
|
||||||
global.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
global.CloudCmd = getCloudCmd({
|
globalThis.CloudCmd = getCloudCmd({
|
||||||
emit,
|
emit,
|
||||||
});
|
});
|
||||||
|
|
||||||
const current = {
|
const current = create();
|
||||||
setAttribute,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 setAttribute = sinon.stub();
|
|
||||||
const link = {};
|
const link = {};
|
||||||
|
|
||||||
global.DOM = getDOM({
|
globalThis.DOM = getDOM({
|
||||||
link
|
link,
|
||||||
});
|
});
|
||||||
|
|
||||||
global.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = {
|
|
||||||
setAttribute,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
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 {
|
const {DOM} = globalThis;
|
||||||
DOM,
|
|
||||||
} = global;
|
|
||||||
|
|
||||||
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,
|
||||||
});
|
});
|
||||||
|
|
@ -130,33 +116,193 @@ 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();
|
||||||
});
|
});
|
||||||
|
|
||||||
function getCloudCmd({emit} = {}) {
|
test('current-file: isCurrentFile: no', (t) => {
|
||||||
return {
|
const {DOM, CloudCmd} = globalThis;
|
||||||
PREFIX: '',
|
|
||||||
emit: emit || sinon.stub(),
|
globalThis.DOM = getDOM();
|
||||||
};
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
}
|
|
||||||
|
const result = currentFile.isCurrentFile();
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
function getDOM({
|
test('current-file: isCurrentFile', (t) => {
|
||||||
link = {},
|
const {DOM, CloudCmd} = globalThis;
|
||||||
getCurrentDirPath = sinon.stub(),
|
|
||||||
getCurrentDirName = sinon.stub(),
|
const isContainClass = stub();
|
||||||
getByDataName = sinon.stub(),
|
|
||||||
} = {}) {
|
globalThis.DOM = getDOM({
|
||||||
|
isContainClass,
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const current = {};
|
||||||
|
currentFile.isCurrentFile(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: getCurrentType', (t) => {
|
||||||
|
const {DOM, CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const {getByDataName} = globalThis.DOM;
|
||||||
|
|
||||||
|
getByDataName.returns({
|
||||||
|
className: 'mini-icon directory',
|
||||||
|
});
|
||||||
|
|
||||||
|
const current = create();
|
||||||
|
|
||||||
|
currentFile.getCurrentType(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.calledWith(getByDataName, ['js-type', current]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
|
||||||
|
const {DOM, CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const {getCurrentType} = globalThis.DOM;
|
||||||
|
|
||||||
|
const current = create();
|
||||||
|
|
||||||
|
currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.calledWith(getCurrentType, [current]);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: isCurrentIsDir: directory', (t) => {
|
||||||
|
const {DOM, CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM({
|
||||||
|
getCurrentType: stub().returns('directory'),
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const current = create();
|
||||||
|
|
||||||
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.ok(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: isCurrentIsDir: directory-link', (t) => {
|
||||||
|
const {DOM, CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM({
|
||||||
|
getCurrentType: stub().returns('directory-link'),
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const current = create();
|
||||||
|
|
||||||
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.ok(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current-file: isCurrentIsDir: file', (t) => {
|
||||||
|
const {DOM, CloudCmd} = globalThis;
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM({
|
||||||
|
getCurrentType: stub().returns('file'),
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const current = create();
|
||||||
|
|
||||||
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCloudCmd = ({emit} = {}) => ({
|
||||||
|
prefix: '',
|
||||||
|
emit: emit || stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = {},
|
||||||
|
getCurrentDirPath = stub(),
|
||||||
|
getCurrentDirName = stub(),
|
||||||
|
getByDataName = stub(),
|
||||||
|
isContainClass = stub(),
|
||||||
|
getCurrentType = stub(),
|
||||||
|
getCurrentPath = stub().returns(''),
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
getCurrentDirName,
|
getCurrentDirName,
|
||||||
|
getCurrentPath,
|
||||||
getByDataName,
|
getByDataName,
|
||||||
|
isContainClass,
|
||||||
|
getCurrentType,
|
||||||
CurrentInfo: {
|
CurrentInfo: {
|
||||||
link,
|
link,
|
||||||
dirPath: '/',
|
dirPath: '/',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,18 @@
|
||||||
'use strict';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import * as smalltalk from 'smalltalk';
|
||||||
|
|
||||||
module.exports = Dialog;
|
const title = 'Cloud Commander';
|
||||||
|
|
||||||
const sm = require('smalltalk');
|
export const alert = (...a) => smalltalk.alert(title, ...a, {
|
||||||
const smNative = require('smalltalk/native');
|
cancel: false,
|
||||||
|
});
|
||||||
|
|
||||||
function Dialog(prefix, config) {
|
export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a);
|
||||||
if (!(this instanceof Dialog))
|
export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a);
|
||||||
return new Dialog(prefix, config);
|
export const progress = (...a) => smalltalk.progress(title, ...a);
|
||||||
|
|
||||||
const {htmlDialogs} = config;
|
|
||||||
const smalltalk = htmlDialogs ? sm : smNative;
|
|
||||||
|
|
||||||
const alert = (title, message) => {
|
|
||||||
return smalltalk.alert(title, message);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.alert = alert;
|
|
||||||
|
|
||||||
this.prompt = (title, message, value, options) => {
|
|
||||||
return smalltalk.prompt(title, message, value, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.confirm = (title, message, options) => {
|
|
||||||
return smalltalk.confirm(title, message, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.alert.noFiles = (title) => {
|
|
||||||
return alert(title, 'No files selected!');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
alert.noFiles = () => {
|
||||||
|
return smalltalk.alert(title, 'No files selected!', {
|
||||||
|
cancel: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,29 @@
|
||||||
/* global CloudCmd */
|
/* global DOM, CloudCmd */
|
||||||
|
import philip from 'philip';
|
||||||
|
import * as Dialog from '#dom/dialog';
|
||||||
|
import {FS} from '#common/cloudfunc';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
|
||||||
'use strict';
|
export const uploadDirectory = (items) => {
|
||||||
|
|
||||||
const philip = require('philip');
|
|
||||||
|
|
||||||
const Images = require('./images');
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
const DOM = require('.');
|
|
||||||
|
|
||||||
const {
|
|
||||||
getCurrentDirPath: getPathWhenRootEmpty,
|
|
||||||
} = DOM;
|
|
||||||
|
|
||||||
module.exports = (items) => {
|
|
||||||
const Dialog = DOM.Dialog;
|
|
||||||
|
|
||||||
if (items.length)
|
if (items.length)
|
||||||
Images.show('top');
|
Images.show('top');
|
||||||
|
|
||||||
const entries = [...items].map((item) => {
|
const entries = Array
|
||||||
return item.webkitGetAsEntry();
|
.from(items)
|
||||||
|
.map((item) => item.webkitGetAsEntry());
|
||||||
|
|
||||||
|
const dirPath = DOM.getCurrentDirPath();
|
||||||
|
const path = dirPath.replace(/\/$/, '');
|
||||||
|
|
||||||
|
const progress = Dialog.progress('Uploading...');
|
||||||
|
|
||||||
|
progress.catch(() => {
|
||||||
|
Dialog.alert('Upload aborted');
|
||||||
|
uploader.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
const dirPath = getPathWhenRootEmpty();
|
|
||||||
const path = dirPath
|
|
||||||
.replace(/\/$/, '');
|
|
||||||
|
|
||||||
const uploader = philip(entries, (type, name, data, i, n, callback) => {
|
const uploader = philip(entries, (type, name, data, i, n, callback) => {
|
||||||
const prefixURL = CloudCmd.PREFIX_URL;
|
const {prefixURL} = CloudCmd;
|
||||||
const full = prefixURL + FS + path + name;
|
const full = prefixURL + FS + path + name;
|
||||||
|
|
||||||
let upload;
|
let upload;
|
||||||
|
|
@ -49,7 +45,7 @@ module.exports = (items) => {
|
||||||
const max = next - current;
|
const max = next - current;
|
||||||
const value = current + percent(count, 100, max);
|
const value = current + percent(count, 100, max);
|
||||||
|
|
||||||
setProgress(value);
|
progress.setProgress(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -58,25 +54,11 @@ module.exports = (items) => {
|
||||||
uploader.abort();
|
uploader.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
uploader.on('progress', setProgress);
|
|
||||||
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 setProgress(count) {
|
const uploadFile = (url, data) => DOM.load.put(url, data);
|
||||||
DOM.Images
|
|
||||||
.setProgress(count)
|
|
||||||
.show('top');
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile(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,6 +1,15 @@
|
||||||
'use strict';
|
import currify from 'currify';
|
||||||
|
|
||||||
const DOM = module.exports;
|
const DOM = {
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
getByClass,
|
||||||
|
getByClassAll,
|
||||||
|
getByDataName,
|
||||||
|
getById,
|
||||||
|
getByTag,
|
||||||
|
isContainClass,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check class of element
|
* check class of element
|
||||||
|
|
@ -8,71 +17,76 @@ const DOM = module.exports;
|
||||||
* @param element
|
* @param element
|
||||||
* @param className
|
* @param className
|
||||||
*/
|
*/
|
||||||
module.exports.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!');
|
||||||
|
|
||||||
if (!className)
|
if (!className)
|
||||||
throw Error('className could not be empty!');
|
throw Error('className could not be empty!');
|
||||||
|
|
||||||
const classList = element.classList;
|
if (Array.isArray(className))
|
||||||
const ret = classList.contains(className);
|
return className.some(currify(
|
||||||
|
isContainClass,
|
||||||
|
element,
|
||||||
|
));
|
||||||
|
|
||||||
return ret;
|
const {classList} = element;
|
||||||
};
|
|
||||||
|
return classList.contains(className);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
|
||||||
152
client/dom/dom-tree.spec.js
Normal file
152
client/dom/dom-tree.spec.js
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import {create} from 'auto-globals';
|
||||||
|
import {tryCatch} from 'try-catch';
|
||||||
|
import {
|
||||||
|
isContainClass,
|
||||||
|
getByTag,
|
||||||
|
getById,
|
||||||
|
getByClass,
|
||||||
|
getByDataName,
|
||||||
|
getByClassAll,
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
} from './dom-tree.js';
|
||||||
|
|
||||||
|
test('dom: isContainClass: no element', (t) => {
|
||||||
|
const [e] = tryCatch(isContainClass);
|
||||||
|
|
||||||
|
t.equal(e.message, 'element could not be empty!', 'should throw when no element');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: isContainClass: no className', (t) => {
|
||||||
|
const [e] = tryCatch(isContainClass, {});
|
||||||
|
|
||||||
|
t.equal(e.message, 'className could not be empty!', 'should throw when no element');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: isContainClass: contains', (t) => {
|
||||||
|
const el = create();
|
||||||
|
const {contains} = el.classList;
|
||||||
|
|
||||||
|
const className = 'hello';
|
||||||
|
isContainClass(el, className);
|
||||||
|
|
||||||
|
t.calledWith(contains, [className], 'should call contains');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: isContainClass: contains: array', (t) => {
|
||||||
|
const el = create();
|
||||||
|
const {contains} = el.classList;
|
||||||
|
|
||||||
|
const className = 'hello';
|
||||||
|
isContainClass(el, ['world', className, 'hello']);
|
||||||
|
|
||||||
|
t.calledWith(contains, [className], 'should call contains');
|
||||||
|
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,222 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const itype = require('itype/legacy');
|
|
||||||
|
|
||||||
module.exports = new EventsProto();
|
|
||||||
|
|
||||||
function EventsProto() {
|
|
||||||
const Events = this;
|
|
||||||
|
|
||||||
function parseArgs(eventName, element, listener, callback) {
|
|
||||||
let isFunc;
|
|
||||||
const args = [
|
|
||||||
eventName,
|
|
||||||
element,
|
|
||||||
listener,
|
|
||||||
callback,
|
|
||||||
];
|
|
||||||
|
|
||||||
const EVENT_NAME = 1;
|
|
||||||
const ELEMENT = 0;
|
|
||||||
const type = itype(eventName);
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
default:
|
|
||||||
if (!/element$/.test(type))
|
|
||||||
throw Error('unknown eventName: ' + type);
|
|
||||||
|
|
||||||
parseArgs(
|
|
||||||
args[EVENT_NAME],
|
|
||||||
args[ELEMENT],
|
|
||||||
listener,
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
isFunc = itype.function(element);
|
|
||||||
|
|
||||||
if (isFunc) {
|
|
||||||
listener = element;
|
|
||||||
element = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!element)
|
|
||||||
element = window;
|
|
||||||
|
|
||||||
callback(element, [
|
|
||||||
eventName,
|
|
||||||
listener,
|
|
||||||
false
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
eventName.forEach((eventName) => {
|
|
||||||
parseArgs(
|
|
||||||
eventName,
|
|
||||||
element,
|
|
||||||
listener,
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'object':
|
|
||||||
Object.keys(eventName).forEach((name) => {
|
|
||||||
const eventListener = eventName[name];
|
|
||||||
|
|
||||||
parseArgs(
|
|
||||||
name,
|
|
||||||
element,
|
|
||||||
eventListener,
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* safe add event listener
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* @param element {document by default}
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
this.add = (type, element, listener) => {
|
|
||||||
checkType(type);
|
|
||||||
|
|
||||||
parseArgs(type, element, listener, (element, args) => {
|
|
||||||
element.addEventListener.apply(element, args);
|
|
||||||
});
|
|
||||||
|
|
||||||
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.apply(element, args);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Events;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
15
client/dom/events/event-store.js
Normal file
15
client/dom/events/event-store.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
export const add = (el, name, fn) => {
|
||||||
|
list.push([
|
||||||
|
el,
|
||||||
|
name,
|
||||||
|
fn,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clear = () => {
|
||||||
|
list = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get = () => list;
|
||||||
37
client/dom/events/event-store.spec.js
Normal file
37
client/dom/events/event-store.spec.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {test} from 'supertape';
|
||||||
|
import * as eventStore from './event-store.js';
|
||||||
|
|
||||||
|
test('event-store: get', (t) => {
|
||||||
|
const el = {};
|
||||||
|
const name = 'click';
|
||||||
|
const fn = () => {};
|
||||||
|
|
||||||
|
eventStore.add(el, name, fn);
|
||||||
|
const result = eventStore.get();
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
[
|
||||||
|
el,
|
||||||
|
name,
|
||||||
|
fn,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('event-store: clear', (t) => {
|
||||||
|
const el = {};
|
||||||
|
const name = 'click';
|
||||||
|
const fn = () => {};
|
||||||
|
|
||||||
|
eventStore.add(el, name, fn);
|
||||||
|
eventStore.clear();
|
||||||
|
|
||||||
|
const result = eventStore.get();
|
||||||
|
const expected = [];
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
203
client/dom/events/index.js
Normal file
203
client/dom/events/index.js
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
import itype from 'itype';
|
||||||
|
import * as EventStore from './event-store.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event listener
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param element - document by default
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
export const add = (type, element, listener) => {
|
||||||
|
checkType(type);
|
||||||
|
|
||||||
|
parseArgs(type, element, listener, (element, args) => {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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) => {
|
||||||
|
if (eventName !== 'touchstart')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
passive: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseArgs(eventName, element, listener, callback) {
|
||||||
|
let isFunc;
|
||||||
|
const args = [
|
||||||
|
eventName,
|
||||||
|
element,
|
||||||
|
listener,
|
||||||
|
callback,
|
||||||
|
];
|
||||||
|
|
||||||
|
const EVENT_NAME = 1;
|
||||||
|
const ELEMENT = 0;
|
||||||
|
const type = itype(eventName);
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
if (!type.endsWith('element'))
|
||||||
|
throw Error(`unknown eventName: ${type}`);
|
||||||
|
|
||||||
|
parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'string':
|
||||||
|
isFunc = itype.function(element);
|
||||||
|
|
||||||
|
if (isFunc) {
|
||||||
|
listener = element;
|
||||||
|
element = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!element)
|
||||||
|
element = window;
|
||||||
|
|
||||||
|
callback(element, [
|
||||||
|
eventName,
|
||||||
|
listener,
|
||||||
|
getEventOptions(eventName),
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'array':
|
||||||
|
|
||||||
|
for (const name of eventName) {
|
||||||
|
parseArgs(name, element, listener, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'object':
|
||||||
|
|
||||||
|
for (const name of Object.keys(eventName)) {
|
||||||
|
const eventListener = eventName[name];
|
||||||
|
|
||||||
|
parseArgs(name, element, eventListener, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Events = {
|
||||||
|
add,
|
||||||
|
addClick,
|
||||||
|
addContextMenu,
|
||||||
|
addKey,
|
||||||
|
addLoad,
|
||||||
|
addOnce,
|
||||||
|
remove,
|
||||||
|
removeAll,
|
||||||
|
rmClick,
|
||||||
|
rmKey,
|
||||||
|
};
|
||||||
|
|
@ -1,105 +1,82 @@
|
||||||
/* 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/legacy');
|
import * as RESTful from '#dom/rest';
|
||||||
const currify = require('currify/legacy');
|
|
||||||
const exec = require('execon');
|
|
||||||
|
|
||||||
const Storage = require('./storage');
|
|
||||||
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) {
|
||||||
|
|
||||||
function getFile(name, callback) {
|
|
||||||
const type = itype(name);
|
const type = itype(name);
|
||||||
let array;
|
check(name);
|
||||||
|
|
||||||
check(name, callback);
|
if (type === 'string')
|
||||||
|
return getModule(name);
|
||||||
|
|
||||||
switch(type) {
|
if (type === 'array')
|
||||||
case 'string':
|
return Promise.all(name.map(getFile));
|
||||||
getModule(name, callback);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
array = unaryMap(name, get);
|
|
||||||
|
|
||||||
exec.parallel(array, callback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function check(name, callback) {
|
function check(name) {
|
||||||
if (!name)
|
if (!name)
|
||||||
throw Error('name could not be empty!');
|
throw Error('name could not be empty!');
|
||||||
|
|
||||||
if (typeof callback !== 'function')
|
|
||||||
throw Error('callback should be a function');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModule(name, callback) {
|
function getModule(name) {
|
||||||
let path;
|
const regExpHTML = RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
||||||
|
const regExpJSON = RegExp(FILES_JSON);
|
||||||
const regExpHTML = new RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
|
||||||
const regExpJSON = new RegExp(FILES_JSON);
|
|
||||||
|
|
||||||
const isHTML = regExpHTML.test(name);
|
const isHTML = regExpHTML.test(name);
|
||||||
const isJSON = regExpJSON.test(name);
|
const isJSON = regExpJSON.test(name);
|
||||||
|
|
||||||
if (!isHTML && !isJSON) {
|
if (!isHTML && !isJSON)
|
||||||
showError(name);
|
return showError(name);
|
||||||
} else if (name === 'config') {
|
|
||||||
getConfig(callback);
|
|
||||||
} else {
|
|
||||||
path = getPath(name, isHTML, isJSON);
|
|
||||||
|
|
||||||
getSystemFile(path, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (name === 'config')
|
||||||
|
return getConfig();
|
||||||
|
|
||||||
|
const path = getPath(name, isHTML, isJSON);
|
||||||
|
|
||||||
|
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) {
|
||||||
if (isRoot)
|
if (isRoot)
|
||||||
path = DIR_HTML + name.replace('-tmpl', '');
|
path = DIR_HTML + name.replace('-tmpl', '');
|
||||||
else
|
else
|
||||||
path = DIR_HTML_FS + name;
|
path = DIR_HTML_FS + name;
|
||||||
|
|
||||||
path += '.hbs';
|
path += '.hbs';
|
||||||
} else if (isJSON) {
|
} else if (isJSON) {
|
||||||
path = DIR_JSON + name + '.json';
|
path = DIR_JSON + name + '.json';
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSystemFile(file, callback) {
|
const getSystemFile = promisify((file, callback) => {
|
||||||
const prefix = CloudCmd.PREFIX;
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
if (!Promises[file])
|
if (!Promises[file])
|
||||||
Promises[file] = new Promise((success, error) => {
|
Promises[file] = new Promise((success, error) => {
|
||||||
|
|
@ -108,7 +85,7 @@ function getSystemFile(file, callback) {
|
||||||
load.ajax({
|
load.ajax({
|
||||||
url,
|
url,
|
||||||
success,
|
success,
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -118,37 +95,29 @@ function getSystemFile(file, callback) {
|
||||||
Promises[file] = null;
|
Promises[file] = null;
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
function getConfig(callback) {
|
const getConfig = async () => {
|
||||||
let is;
|
let is;
|
||||||
|
|
||||||
if (!Promises.config)
|
if (!Promises.config)
|
||||||
Promises.config = new Promise((resolve, reject) => {
|
Promises.config = () => {
|
||||||
is = true;
|
is = true;
|
||||||
RESTful.Config.read((error, data) => {
|
return RESTful.Config.read();
|
||||||
if (error)
|
};
|
||||||
return reject(error);
|
|
||||||
|
|
||||||
resolve(data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Promises.config.then((data) => {
|
const [, data] = await Promises.config();
|
||||||
|
|
||||||
|
if (data)
|
||||||
is = false;
|
is = false;
|
||||||
Storage.setAllowed(data.localStorage);
|
|
||||||
|
timeout(() => {
|
||||||
callback(null, data);
|
|
||||||
|
|
||||||
timeout(() => {
|
|
||||||
if (!is)
|
|
||||||
Promises.config = null;
|
|
||||||
});
|
|
||||||
}, () => {
|
|
||||||
if (!is)
|
if (!is)
|
||||||
Promises.config = null;
|
Promises.config = null;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
function getTimeoutOnce(time) {
|
function getTimeoutOnce(time) {
|
||||||
let is;
|
let is;
|
||||||
|
|
@ -165,4 +134,3 @@ function getTimeoutOnce(time) {
|
||||||
}, time);
|
}, time);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
/* global CloudCmd */
|
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
import createElement from '@cloudcmd/create-element';
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const Images = module.exports;
|
|
||||||
|
|
||||||
const LOADING = 'loading';
|
const LOADING = 'loading';
|
||||||
const HIDDEN = 'hidden';
|
const HIDDEN = 'hidden';
|
||||||
const ERROR = 'error';
|
const ERROR = 'error';
|
||||||
|
|
||||||
const LoadingImage = LOADING + getLoadingType();
|
const getLoadingType = () => isSVG() ? '-svg' : '-gif';
|
||||||
|
|
||||||
function getLoadingType() {
|
export const get = getElement;
|
||||||
return isSVG() ? '-svg' : '-gif';
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.get = getElement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check SVG SMIL animation support
|
* check SVG SMIL animation support
|
||||||
|
|
@ -34,54 +26,60 @@ function isSVG() {
|
||||||
return /SVGAnimate/.test(name);
|
return /SVGAnimate/.test(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getElement() {
|
function getElement() {
|
||||||
return DOM.load({
|
return createElement('span', {
|
||||||
name : 'span',
|
id: 'js-status-image',
|
||||||
id : 'js-status-image',
|
className: 'icon',
|
||||||
className : 'icon',
|
dataName: 'progress',
|
||||||
attribute : 'data-progress',
|
notAppend: true,
|
||||||
notAppend : true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Функция создаёт картинку загрузки */
|
/* Функция создаёт картинку загрузки */
|
||||||
module.exports.loading = () => {
|
export const loading = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const classList = element.classList;
|
const {classList} = element;
|
||||||
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
||||||
classList.add(LOADING, LoadingImage);
|
classList.add(LOADING, loadingImage);
|
||||||
classList.remove(ERROR, HIDDEN);
|
classList.remove(ERROR, HIDDEN);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Функция создаёт картинку ошибки загрузки */
|
/* Функция создаёт картинку ошибки загрузки */
|
||||||
module.exports.error = () => {
|
export const error = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const classList = element.classList;
|
const {classList} = element;
|
||||||
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
||||||
classList.add(ERROR);
|
classList.add(ERROR);
|
||||||
classList.remove(HIDDEN, LOADING, LoadingImage);
|
classList.remove(HIDDEN, LOADING, loadingImage);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
let current;
|
let current;
|
||||||
|
|
||||||
if (position === 'top') {
|
if (position === 'top') {
|
||||||
current = refreshButton.parentElement;
|
current = refreshButton.parentElement;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -93,7 +91,7 @@ function show(position, panel) {
|
||||||
current = refreshButton.parentElement;
|
current = refreshButton.parentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parent || (parent && parent !== current))
|
if (!parent || parent && parent !== current)
|
||||||
current.appendChild(image);
|
current.appendChild(image);
|
||||||
|
|
||||||
DOM.show(image);
|
DOM.show(image);
|
||||||
|
|
@ -101,36 +99,25 @@ function show(position, panel) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(text) {
|
|
||||||
const image = Images.error();
|
|
||||||
|
|
||||||
DOM.show(image);
|
|
||||||
image.title = text;
|
|
||||||
|
|
||||||
CloudCmd.log(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;
|
||||||
|
|
@ -138,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();
|
||||||
|
|
||||||
|
|
@ -151,3 +138,12 @@ module.exports.clearProgress = () => {
|
||||||
return Images;
|
return Images;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Images = {
|
||||||
|
clearProgress,
|
||||||
|
setProgress,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
get,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
|
|
||||||
1761
client/dom/index.js
1761
client/dom/index.js
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();
|
||||||
|
});
|
||||||
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');
|
||||||
|
}
|
||||||
11
client/dom/io/send-request.spec.js
Normal file
11
client/dom/io/send-request.spec.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {test} from 'supertape';
|
||||||
|
import {_replaceHash} from './send-request.js';
|
||||||
|
|
||||||
|
test('cloudcmd: client: io: replaceHash', (t) => {
|
||||||
|
const url = '/hello/####world';
|
||||||
|
const result = _replaceHash(url);
|
||||||
|
const expected = '/hello/%23%23%23%23world';
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,69 +1,58 @@
|
||||||
'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 exec = require('execon');
|
export const loadRemote = callbackify(async (name, options) => {
|
||||||
const rendy = require('rendy/legacy');
|
const {prefix, config} = CloudCmd;
|
||||||
const itype = require('itype/legacy');
|
|
||||||
const {findObjByNameInArr} = require('../../common/util');
|
|
||||||
|
|
||||||
const load = require('./load');
|
|
||||||
const Files = require('./files');
|
|
||||||
|
|
||||||
module.exports = (name, options, callback = options) => {
|
|
||||||
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;
|
||||||
|
|
||||||
Files.get('modules', (error, modules) => {
|
const modules = await Files.get('modules');
|
||||||
const online = config('online') && navigator.onLine;
|
|
||||||
const module = findObjByNameInArr(modules.remote, name);
|
const online = config('online') && navigator.onLine;
|
||||||
|
const module = findObjByNameInArr(modules.remote, name);
|
||||||
|
|
||||||
|
const isArray = itype.array(module.local);
|
||||||
|
const {version} = module;
|
||||||
|
|
||||||
|
let remoteTmpls;
|
||||||
|
let local;
|
||||||
|
|
||||||
|
if (isArray) {
|
||||||
|
remoteTmpls = module.remote;
|
||||||
|
({local} = module);
|
||||||
|
} else {
|
||||||
|
remoteTmpls = [module.remote];
|
||||||
|
local = [module.local];
|
||||||
|
}
|
||||||
|
|
||||||
|
const localURL = [];
|
||||||
|
|
||||||
|
for (const url of local) {
|
||||||
|
localURL.push(prefix + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteURL = [];
|
||||||
|
|
||||||
|
for (const tmpl of remoteTmpls) {
|
||||||
|
remoteURL.push(rendy(tmpl, {
|
||||||
|
version,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (online) {
|
||||||
|
const [e] = await tryToCatch(load.parallel, remoteURL);
|
||||||
|
|
||||||
const isArray = itype.array(module.local);
|
if (!e)
|
||||||
const version = module.version;
|
return;
|
||||||
|
}
|
||||||
let remoteTmpls, local;
|
|
||||||
if (isArray) {
|
await load.parallel(localURL);
|
||||||
remoteTmpls = module.remote;
|
});
|
||||||
local = module.local;
|
|
||||||
} else {
|
|
||||||
remoteTmpls = [module.remote];
|
|
||||||
local = [module.local];
|
|
||||||
}
|
|
||||||
|
|
||||||
const localURL = local.map((url) => {
|
|
||||||
return PREFIX + url;
|
|
||||||
});
|
|
||||||
|
|
||||||
const remoteURL = remoteTmpls.map((tmpl) => {
|
|
||||||
return rendy(tmpl, {
|
|
||||||
version: version
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const on = funcON(localURL, remoteURL, callback);
|
|
||||||
const off = funcOFF(localURL, callback);
|
|
||||||
|
|
||||||
exec.if(online, on, off);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function funcOFF(local, callback) {
|
|
||||||
return () => {
|
|
||||||
load.parallel(local, callback);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function funcON (local, remote,callback) {
|
|
||||||
return () => {
|
|
||||||
load.parallel(remote, (error) => {
|
|
||||||
if (error)
|
|
||||||
return funcOFF();
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,130 +1,8 @@
|
||||||
'use strict';
|
import itype from 'itype';
|
||||||
|
import jonny from 'jonny';
|
||||||
const itype = require('itype/legacy');
|
import Emitify from 'emitify';
|
||||||
const jonny = require('jonny/legacy');
|
import exec from 'execon';
|
||||||
const Emitify = require('emitify/legacy');
|
import * as Images from '#dom/images';
|
||||||
const exec = require('execon');
|
|
||||||
const Images = require('./images');
|
|
||||||
const Events = require('./events');
|
|
||||||
|
|
||||||
const {getExt} = require('../../common/util');
|
|
||||||
|
|
||||||
module.exports = load;
|
|
||||||
module.exports.getIdBySrc = getIdBySrc;
|
|
||||||
module.exports.ext = ext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Функция создаёт элемент и загружает файл с src.
|
|
||||||
*
|
|
||||||
* @param params = {
|
|
||||||
* name, - название тэга
|
|
||||||
* src', - путь к файлу
|
|
||||||
* func, - обьект, содержаий одну из функций
|
|
||||||
* или сразу две onload и onerror
|
|
||||||
* {onload: function() {}, onerror: function();}
|
|
||||||
* style,
|
|
||||||
* id,
|
|
||||||
* element,
|
|
||||||
* async, - true by default
|
|
||||||
* inner: 'id{color:red, },
|
|
||||||
* class,
|
|
||||||
* notAppend - false by default
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function load(params) {
|
|
||||||
const {
|
|
||||||
src,
|
|
||||||
id = getIdBySrc(params.src),
|
|
||||||
func,
|
|
||||||
name,
|
|
||||||
async,
|
|
||||||
inner,
|
|
||||||
style,
|
|
||||||
parent = document.body,
|
|
||||||
className,
|
|
||||||
attribute,
|
|
||||||
notAppend,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
let element = document.getElementById(id);
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
exec(func);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
element = document.createElement(name);
|
|
||||||
|
|
||||||
const funcError = () => {
|
|
||||||
const msg = `file ${src} could not be loaded`;
|
|
||||||
const error = new Error(msg);
|
|
||||||
|
|
||||||
parent.removeChild(element);
|
|
||||||
|
|
||||||
Images.show.error(msg);
|
|
||||||
|
|
||||||
const callback = func && func.onerror || func.onload || func;
|
|
||||||
|
|
||||||
exec(callback, error);
|
|
||||||
};
|
|
||||||
|
|
||||||
const funcLoad = () => {
|
|
||||||
const callback = func && func.onload || func;
|
|
||||||
|
|
||||||
Events.remove('error', element, funcError);
|
|
||||||
|
|
||||||
exec(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (/^(script|link)$/.test(name))
|
|
||||||
Events.addOnce('load', element, funcLoad)
|
|
||||||
.addError(element, funcError);
|
|
||||||
|
|
||||||
if (id)
|
|
||||||
element.id = id;
|
|
||||||
|
|
||||||
if (className)
|
|
||||||
element.className = className;
|
|
||||||
|
|
||||||
if (src) {
|
|
||||||
if (name !== 'link') {
|
|
||||||
element.src = src;
|
|
||||||
} else {
|
|
||||||
element.href = src;
|
|
||||||
element.rel = 'stylesheet';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attribute) {
|
|
||||||
const type = itype(attribute);
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 'string':
|
|
||||||
element.setAttribute(attribute, '');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'object':
|
|
||||||
Object.keys(attribute).forEach((name) => {
|
|
||||||
element.setAttribute(name, attribute[name]);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (style)
|
|
||||||
element.style.cssText = style;
|
|
||||||
|
|
||||||
if (async && name === 'script' || async === undefined)
|
|
||||||
element.async = true;
|
|
||||||
|
|
||||||
if (!notAppend)
|
|
||||||
parent.appendChild(element);
|
|
||||||
|
|
||||||
if (inner)
|
|
||||||
element.innerHTML = inner;
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function gets id by src
|
* Function gets id by src
|
||||||
|
|
@ -132,17 +10,18 @@ function load(params) {
|
||||||
*
|
*
|
||||||
* 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, '-');
|
||||||
|
|
@ -155,34 +34,37 @@ 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 = p.headers || {};
|
|
||||||
|
const {headers = {}} = p;
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.open(type, p.url, true);
|
xhr.open(type, p.url, true);
|
||||||
|
|
||||||
Object.keys(headers).forEach((name) => {
|
for (const name of Object.keys(headers)) {
|
||||||
const value = headers[name];
|
const value = headers[name];
|
||||||
xhr.setRequestHeader(name, value);
|
xhr.setRequestHeader(name, value);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (p.responseType)
|
if (p.responseType)
|
||||||
xhr.responseType = p.responseType;
|
xhr.responseType = p.responseType;
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
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;
|
||||||
const OK = 200;
|
const OK = 200;
|
||||||
|
|
||||||
if (xhr.readyState !== xhr.DONE)
|
if (xhr.readyState !== xhr.DONE)
|
||||||
return;
|
return;
|
||||||
|
|
@ -190,30 +72,30 @@ module.exports.ajax = (params) => {
|
||||||
Images.clearProgress();
|
Images.clearProgress();
|
||||||
|
|
||||||
const TYPE_JSON = 'application/json';
|
const TYPE_JSON = 'application/json';
|
||||||
const type = xhr.getResponseHeader('content-type');
|
const type = xhr.getResponseHeader('content-type');
|
||||||
|
|
||||||
if (xhr.status !== OK)
|
if (xhr.status !== OK)
|
||||||
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;
|
||||||
|
|
||||||
if (type && isContain && notText)
|
if (type && isContain && notText)
|
||||||
data = jonny.parse(xhr.response) || xhr.response;
|
data = jonny.parse(xhr.response) || xhr.response;
|
||||||
|
|
||||||
exec(p.success, data, xhr.statusText, xhr);
|
exec(p.success, data, xhr.statusText, xhr);
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
@ -221,8 +103,8 @@ module.exports.put = (url, body) => {
|
||||||
if (!event.lengthComputable)
|
if (!event.lengthComputable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const percent = (event.loaded / event.total) * 100;
|
const percent = event.loaded / event.total * 100;
|
||||||
const count = Math.round(percent);
|
const count = Math.round(percent);
|
||||||
|
|
||||||
emitter.emit('progress', count);
|
emitter.emit('progress', count);
|
||||||
};
|
};
|
||||||
|
|
@ -234,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);
|
||||||
|
|
@ -245,117 +129,3 @@ module.exports.put = (url, body) => {
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ext(src, func) {
|
|
||||||
switch (getExt(src)) {
|
|
||||||
case '.js':
|
|
||||||
return load.js(src, func);
|
|
||||||
|
|
||||||
case '.css':
|
|
||||||
return load.css(src, func);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return load({
|
|
||||||
src,
|
|
||||||
func,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create elements and load them to DOM-tree
|
|
||||||
* one-by-one
|
|
||||||
*
|
|
||||||
* @param params
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
load.series = (params, callback) => {
|
|
||||||
if (!params)
|
|
||||||
return load;
|
|
||||||
|
|
||||||
const funcs = params
|
|
||||||
.map((url) => ext.bind(null, url))
|
|
||||||
.concat(callback);
|
|
||||||
|
|
||||||
exec.series(funcs);
|
|
||||||
|
|
||||||
return load;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* improve callback of funcs so
|
|
||||||
* we pop number of function and
|
|
||||||
* if it's last we call pCallBack
|
|
||||||
*
|
|
||||||
* @param params
|
|
||||||
* @param callback - onload function
|
|
||||||
*/
|
|
||||||
load.parallel = (params, callback) => {
|
|
||||||
if (!params)
|
|
||||||
return load;
|
|
||||||
|
|
||||||
const funcs = params.map((url) => {
|
|
||||||
return ext.bind(null, url);
|
|
||||||
});
|
|
||||||
|
|
||||||
exec.parallel(funcs, callback);
|
|
||||||
|
|
||||||
return load;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Функция загружает js-файл
|
|
||||||
*
|
|
||||||
* @param src
|
|
||||||
* @param func
|
|
||||||
*/
|
|
||||||
load.js = (src, func) => {
|
|
||||||
const name = 'script';
|
|
||||||
|
|
||||||
return load({
|
|
||||||
name,
|
|
||||||
src,
|
|
||||||
func,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
load.css = (src, func) => {
|
|
||||||
const name = 'link';
|
|
||||||
const {head:parent} = document;
|
|
||||||
|
|
||||||
return load({
|
|
||||||
name,
|
|
||||||
src,
|
|
||||||
parent,
|
|
||||||
func
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Функция создаёт елемент style и записывает туда стили
|
|
||||||
* @param params - структура параметров, заполняеться таким
|
|
||||||
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
|
|
||||||
* все параметры опциональны
|
|
||||||
*/
|
|
||||||
load.style = (params) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
src,
|
|
||||||
name = 'style',
|
|
||||||
func,
|
|
||||||
inner,
|
|
||||||
parent = document.head,
|
|
||||||
element,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
return load({
|
|
||||||
id,
|
|
||||||
src,
|
|
||||||
func,
|
|
||||||
name,
|
|
||||||
inner,
|
|
||||||
parent,
|
|
||||||
element,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
||||||
66
client/dom/operations/rename-current.js
Normal file
66
client/dom/operations/rename-current.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* 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';
|
||||||
|
|
||||||
|
export default async (current, overrides = {}) => {
|
||||||
|
const {
|
||||||
|
refresh = CloudCmd.refresh,
|
||||||
|
Dialog = _Dialog,
|
||||||
|
currentFile = _currentFile,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
|
const {
|
||||||
|
isCurrentFile,
|
||||||
|
getCurrentName,
|
||||||
|
getCurrentFile,
|
||||||
|
getCurrentByName,
|
||||||
|
getCurrentType,
|
||||||
|
getCurrentDirPath,
|
||||||
|
setCurrentName,
|
||||||
|
} = currentFile;
|
||||||
|
|
||||||
|
if (!isCurrentFile(current))
|
||||||
|
current = getCurrentFile();
|
||||||
|
|
||||||
|
const from = getCurrentName(current);
|
||||||
|
|
||||||
|
if (from === '..')
|
||||||
|
return Dialog.alert.noFiles();
|
||||||
|
|
||||||
|
const [cancel, to] = await Dialog.prompt('Rename', from);
|
||||||
|
|
||||||
|
if (cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const nextFile = getCurrentByName(to);
|
||||||
|
|
||||||
|
if (nextFile) {
|
||||||
|
const type = getCurrentType(nextFile);
|
||||||
|
const msg = `${capitalize(type)} "${to}" already exists. Proceed?`;
|
||||||
|
const [cancel] = await Dialog.confirm(msg);
|
||||||
|
|
||||||
|
if (cancel)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from === to)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const dirPath = getCurrentDirPath();
|
||||||
|
|
||||||
|
const fromFull = `${dirPath}${from}`;
|
||||||
|
const toFull = `${dirPath}${to}`;
|
||||||
|
|
||||||
|
const [e] = await RESTful.rename(fromFull, toFull);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setCurrentName(to, current);
|
||||||
|
|
||||||
|
Storage.remove(dirPath);
|
||||||
|
refresh();
|
||||||
|
};
|
||||||
86
client/dom/operations/rename-current.spec.js
Normal file
86
client/dom/operations/rename-current.spec.js
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import renameCurrent from './rename-current.js';
|
||||||
|
|
||||||
|
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
|
||||||
|
const current = {};
|
||||||
|
const isCurrentFile = stub();
|
||||||
|
|
||||||
|
const currentFile = stubCurrentFile({
|
||||||
|
isCurrentFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
await renameCurrent(current, {
|
||||||
|
Dialog: stubDialog(),
|
||||||
|
currentFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
|
||||||
|
const current = {};
|
||||||
|
const name = 'hello';
|
||||||
|
|
||||||
|
const prompt = stub().returns([null, name]);
|
||||||
|
const confirm = stub().returns([true]);
|
||||||
|
|
||||||
|
const getCurrentByName = stub().returns(current);
|
||||||
|
const getCurrentType = stub().returns('directory');
|
||||||
|
|
||||||
|
const Dialog = stubDialog({
|
||||||
|
confirm,
|
||||||
|
prompt,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentFile = stubCurrentFile({
|
||||||
|
getCurrentByName,
|
||||||
|
getCurrentType,
|
||||||
|
});
|
||||||
|
|
||||||
|
await renameCurrent(null, {
|
||||||
|
Dialog,
|
||||||
|
currentFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = 'Directory "hello" already exists. Proceed?';
|
||||||
|
|
||||||
|
t.calledWith(confirm, [expected], 'should call confirm');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
const stubDialog = (fns = {}) => {
|
||||||
|
const {
|
||||||
|
alert = stub().returns([]),
|
||||||
|
confirm = stub().returns([]),
|
||||||
|
prompt = stub().returns([]),
|
||||||
|
} = fns;
|
||||||
|
|
||||||
|
return {
|
||||||
|
alert,
|
||||||
|
confirm,
|
||||||
|
prompt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const stubCurrentFile = (fns = {}) => {
|
||||||
|
const {
|
||||||
|
isCurrentFile = stub(),
|
||||||
|
getCurrentName = stub(),
|
||||||
|
getCurrentFile = stub(),
|
||||||
|
getCurrentByName = stub(),
|
||||||
|
getCurrentType = stub(),
|
||||||
|
getCurrentDirPath = stub(),
|
||||||
|
setCurrentName = stub(),
|
||||||
|
} = fns;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isCurrentFile,
|
||||||
|
getCurrentName,
|
||||||
|
getCurrentFile,
|
||||||
|
getCurrentByName,
|
||||||
|
getCurrentType,
|
||||||
|
getCurrentDirPath,
|
||||||
|
setCurrentName,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,220 +1,40 @@
|
||||||
'use strict';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
|
import * as Dialog from '#dom/dialog';
|
||||||
|
import * as Images from '#dom/images';
|
||||||
|
import {encode} from '#common/entity';
|
||||||
|
import * as IO from './io/index.js';
|
||||||
|
|
||||||
/* global CloudCmd, DOM */
|
const handleError = (promise) => async (...args) => {
|
||||||
|
const [e, data] = await tryToCatch(promise, ...args);
|
||||||
|
|
||||||
|
if (!e)
|
||||||
|
return [e, data];
|
||||||
|
|
||||||
|
const encoded = encode(e.message);
|
||||||
|
|
||||||
|
Images.show.error(encoded);
|
||||||
|
Dialog.alert(encoded);
|
||||||
|
|
||||||
|
return [e, data];
|
||||||
|
};
|
||||||
|
|
||||||
const itype = require('itype/legacy');
|
export const remove = handleError(IO.remove);
|
||||||
|
export const patch = handleError(IO.patch);
|
||||||
|
export const write = handleError(IO.write);
|
||||||
|
export const createDirectory = handleError(IO.createDirectory);
|
||||||
|
export const read = handleError(IO.read);
|
||||||
|
export const copy = handleError(IO.copy);
|
||||||
|
export const pack = handleError(IO.pack);
|
||||||
|
export const extract = handleError(IO.extract);
|
||||||
|
export const move = handleError(IO.move);
|
||||||
|
export const rename = handleError(IO.rename);
|
||||||
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
export const Config = {
|
||||||
const {
|
read: handleError(IO.Config.read),
|
||||||
encode,
|
write: handleError(IO.Config.write),
|
||||||
} = require('../../common/entity');
|
};
|
||||||
|
|
||||||
module.exports = new RESTful();
|
export const Markdown = {
|
||||||
|
read: handleError(IO.Markdown.read),
|
||||||
const Images = require('./images');
|
render: handleError(IO.Markdown.render),
|
||||||
const load = require('./load');
|
};
|
||||||
|
|
||||||
function RESTful() {
|
|
||||||
this.delete = (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 }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.patch = (url, data, callback) => {
|
|
||||||
const isFunc = itype.function(data);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = data;
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imgPosition = {
|
|
||||||
top: true
|
|
||||||
};
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.write = (url, data, callback) => {
|
|
||||||
const isFunc = itype.function(data);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = data;
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition : { top: true }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.read = (url, dataType, callback) => {
|
|
||||||
const isQuery = /\?/.test(url);
|
|
||||||
const isBeautify = /\?beautify$/.test(url);
|
|
||||||
const isMinify = /\?minify$/.test(url);
|
|
||||||
const notLog = !isQuery || isBeautify || isMinify;
|
|
||||||
const isFunc = itype.function(dataType);
|
|
||||||
|
|
||||||
if (!callback && isFunc) {
|
|
||||||
callback = dataType;
|
|
||||||
dataType = 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: FS + url,
|
|
||||||
callback,
|
|
||||||
notLog,
|
|
||||||
dataType,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.cp = (data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/cp',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
imgPosition : { top: true }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.pack = (data, callback) => {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/pack',
|
|
||||||
data,
|
|
||||||
callback,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.extract = function(data, callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/extract',
|
|
||||||
data : data,
|
|
||||||
callback : callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.mv = function(data, callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/mv',
|
|
||||||
data : data,
|
|
||||||
callback : callback,
|
|
||||||
imgPosition : { top: true }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.Config = {
|
|
||||||
read: function(callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'GET',
|
|
||||||
url : '/config',
|
|
||||||
callback : callback,
|
|
||||||
imgPosition : { top: true },
|
|
||||||
notLog : true
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
write: function(data, callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PATCH',
|
|
||||||
url : '/config',
|
|
||||||
data : data,
|
|
||||||
callback : callback,
|
|
||||||
imgPosition : { top: true }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.Markdown = {
|
|
||||||
read : function(url, callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'GET',
|
|
||||||
url : '/markdown' + url,
|
|
||||||
callback : callback,
|
|
||||||
imgPosition : { top: true },
|
|
||||||
notLog : true
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function(data, callback) {
|
|
||||||
sendRequest({
|
|
||||||
method : 'PUT',
|
|
||||||
url : '/markdown',
|
|
||||||
data : data,
|
|
||||||
callback : callback,
|
|
||||||
imgPosition : { top: true },
|
|
||||||
notLog : true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function sendRequest(params) {
|
|
||||||
const p = params;
|
|
||||||
const prefixUrl = CloudCmd.PREFIX_URL;
|
|
||||||
|
|
||||||
p.url = prefixUrl + p.url;
|
|
||||||
p.url = encodeURI(p.url);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if we send ajax request -
|
|
||||||
* no need in hash so we escape #
|
|
||||||
*/
|
|
||||||
p.url = p.url.replace('#', '%23');
|
|
||||||
|
|
||||||
load.ajax({
|
|
||||||
method : p.method,
|
|
||||||
url : p.url,
|
|
||||||
data : p.data,
|
|
||||||
dataType : p.dataType,
|
|
||||||
error : (jqXHR) => {
|
|
||||||
const response = jqXHR.responseText;
|
|
||||||
const statusText = jqXHR.statusText;
|
|
||||||
const status = jqXHR.status;
|
|
||||||
const text = status === 404 ? response : statusText;
|
|
||||||
const encoded = encode(text);
|
|
||||||
|
|
||||||
Images.show.error(encoded);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
DOM.Dialog.alert(CloudCmd.TITLE, encoded);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
p.callback(Error(text));
|
|
||||||
},
|
|
||||||
success: (data) => {
|
|
||||||
Images.hide();
|
|
||||||
|
|
||||||
if (!p.notLog)
|
|
||||||
CloudCmd.log(data);
|
|
||||||
|
|
||||||
p.callback(null, data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,46 @@
|
||||||
'use strict';
|
import {alert, prompt} from '#dom/dialog';
|
||||||
|
import {getRegExp} from '#common/util';
|
||||||
/* global DOM */
|
import {getCurrentName} from './current-file.js';
|
||||||
|
import {
|
||||||
|
isSelected,
|
||||||
|
toggleSelectedFile,
|
||||||
|
} from './cmd.js';
|
||||||
|
|
||||||
let SelectType = '*.*';
|
let SelectType = '*.*';
|
||||||
const TITLE = 'Cloud Commander';
|
|
||||||
|
|
||||||
const {getRegExp} = require('../../common/util');
|
export const selectByPattern = async (msg, files) => {
|
||||||
|
if (!files)
|
||||||
module.exports = (msg, files) => {
|
return;
|
||||||
const allMsg = `Specify file type for ${msg} selection`;
|
|
||||||
const cancel = false;
|
|
||||||
const {Dialog} = DOM;
|
|
||||||
|
|
||||||
Dialog.prompt(TITLE, allMsg, SelectType, {cancel}).then((type) => {
|
const allMsg = `Specify file type for ${msg} selection`;
|
||||||
SelectType = type;
|
const [cancel, type] = await prompt(allMsg, SelectType);
|
||||||
|
|
||||||
|
if (cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SelectType = type;
|
||||||
|
|
||||||
|
const regExp = getRegExp(type);
|
||||||
|
let matches = 0;
|
||||||
|
|
||||||
|
for (const current of files) {
|
||||||
|
const name = getCurrentName(current);
|
||||||
|
|
||||||
const regExp = getRegExp(type);
|
if (name === '..' || !regExp.test(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!files)
|
++matches;
|
||||||
return;
|
|
||||||
|
|
||||||
let matches = 0;
|
let selected = isSelected(current);
|
||||||
|
const shouldSel = msg === 'expand';
|
||||||
|
|
||||||
files.forEach((current) => {
|
if (shouldSel)
|
||||||
const name = DOM.getCurrentName(current);
|
selected = !selected;
|
||||||
|
|
||||||
if (name === '..')
|
|
||||||
return;
|
|
||||||
|
|
||||||
const isMatch = regExp.test(name);
|
|
||||||
|
|
||||||
if (!isMatch)
|
|
||||||
return;
|
|
||||||
|
|
||||||
++matches;
|
|
||||||
|
|
||||||
let isSelected = DOM.isSelected(current);
|
|
||||||
const shouldSel = msg === 'expand';
|
|
||||||
|
|
||||||
if (shouldSel)
|
|
||||||
isSelected = !isSelected;
|
|
||||||
|
|
||||||
if (isSelected)
|
|
||||||
DOM.toggleSelectedFile(current);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!matches)
|
if (selected)
|
||||||
Dialog.alert('Select Files', 'No matches found!');
|
toggleSelectedFile(current);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (!matches)
|
||||||
|
alert('No matches found!');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,26 @@
|
||||||
'use strict';
|
const {parse, stringify} = JSON;
|
||||||
|
|
||||||
const itype = require('itype/legacy');
|
export const set = (name, data) => {
|
||||||
const jonny = require('jonny/legacy');
|
localStorage.setItem(name, data);
|
||||||
const exec = require('execon');
|
|
||||||
const tryCatch = require('try-catch');
|
|
||||||
const setItem = localStorage.setItem.bind(localStorage);
|
|
||||||
|
|
||||||
/* приватный переключатель возможности работы с кэшем */
|
|
||||||
let Allowed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* allow Storage usage
|
|
||||||
*/
|
|
||||||
module.exports.setAllowed = (isAllowed) => {
|
|
||||||
Allowed = isAllowed;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** remove element */
|
export const setJson = (name, data) => {
|
||||||
module.exports.remove = (item, callback) => {
|
localStorage.setItem(name, stringify(data));
|
||||||
if (Allowed)
|
|
||||||
localStorage.removeItem(item);
|
|
||||||
|
|
||||||
exec(callback, null, Allowed);
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.removeMatch = (string, callback) => {
|
export const get = (name) => {
|
||||||
const reg = RegExp('^' + string + '.*$');
|
return localStorage.getItem(name);
|
||||||
const test = (a) => reg.test(a);
|
|
||||||
const remove = (a) => localStorage.removeItem(a);
|
|
||||||
|
|
||||||
Object.keys(localStorage)
|
|
||||||
.filter(test)
|
|
||||||
.forEach(remove);
|
|
||||||
|
|
||||||
exec(callback);
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** если доступен localStorage и
|
export const getJson = (name) => {
|
||||||
* в нём есть нужная нам директория -
|
const data = localStorage.getItem(name);
|
||||||
* записываем данные в него
|
return parse(data);
|
||||||
*/
|
|
||||||
module.exports.set = (name, data, callback) => {
|
|
||||||
let str, error;
|
|
||||||
|
|
||||||
if (itype.object(data))
|
|
||||||
str = jonny.stringify(data);
|
|
||||||
|
|
||||||
if (Allowed && name)
|
|
||||||
[error] = tryCatch(setItem, name, str || data);
|
|
||||||
|
|
||||||
exec(callback, error);
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Если доступен Storage принимаем из него данные*/
|
|
||||||
module.exports.get = (name, callback) => {
|
|
||||||
let ret;
|
|
||||||
|
|
||||||
if (Allowed)
|
|
||||||
ret = localStorage.getItem(name);
|
|
||||||
|
|
||||||
exec(callback, null, ret);
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** функция чистит весь кэш для всех каталогов*/
|
|
||||||
module.exports.clear = (callback) => {
|
|
||||||
const ret = Allowed;
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
localStorage.clear();
|
|
||||||
|
|
||||||
exec(callback, null, ret);
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clear = () => {
|
||||||
|
localStorage.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = (item) => {
|
||||||
|
localStorage.removeItem(item);
|
||||||
|
};
|
||||||
|
|
|
||||||
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,27 +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/legacy');
|
|
||||||
|
|
||||||
const DOM = require('.');
|
|
||||||
const load = require('./load');
|
|
||||||
const Images = require('./images');
|
|
||||||
|
|
||||||
const {FS} = require('../../common/cloudfunc');
|
|
||||||
|
|
||||||
const onEnd = wraptile(_onEnd);
|
|
||||||
const loadFile = wraptile(_loadFile);
|
const loadFile = wraptile(_loadFile);
|
||||||
|
const onEnd = wraptile(_onEnd);
|
||||||
|
|
||||||
const {
|
export const uploadFiles = (dir, files) => {
|
||||||
getCurrentDirPath: getPathWhenRootEmpty
|
|
||||||
} = DOM;
|
|
||||||
|
|
||||||
module.exports = (dir, files) => {
|
|
||||||
if (!files) {
|
if (!files) {
|
||||||
files = dir;
|
files = dir;
|
||||||
dir = getPathWhenRootEmpty();
|
dir = getCurrentDirPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = files.length;
|
const n = files.length;
|
||||||
|
|
@ -29,7 +21,7 @@ module.exports = (dir, files) => {
|
||||||
if (!n)
|
if (!n)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const array = [...files];
|
const array = Array.from(files);
|
||||||
const {name} = files[0];
|
const {name} = files[0];
|
||||||
|
|
||||||
eachSeries(array, loadFile(dir, n), onEnd(name));
|
eachSeries(array, loadFile(dir, n), onEnd(name));
|
||||||
|
|
@ -37,17 +29,17 @@ module.exports = (dir, files) => {
|
||||||
|
|
||||||
function _onEnd(currentName) {
|
function _onEnd(currentName) {
|
||||||
CloudCmd.refresh({
|
CloudCmd.refresh({
|
||||||
currentName
|
currentName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadFile(dir, n, file, callback) {
|
function _loadFile(dir, n, file, callback) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
const name = file.name;
|
const {name} = file;
|
||||||
const path = dir + name;
|
const path = dir + name;
|
||||||
const {PREFIX_URL} = CloudCmd;
|
const {prefixURL} = CloudCmd;
|
||||||
const api = PREFIX_URL + FS;
|
const api = prefixURL + FS;
|
||||||
|
|
||||||
const percent = (i, n, per = 100) => {
|
const percent = (i, n, per = 100) => {
|
||||||
return Math.round(i * per / n);
|
return Math.round(i * per / n);
|
||||||
|
|
@ -57,7 +49,9 @@ function _loadFile(dir, n, file, callback) {
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
load.put(api + path, file)
|
load
|
||||||
|
.put(api + path, file)
|
||||||
|
.on('error', showError)
|
||||||
.on('end', callback)
|
.on('end', callback)
|
||||||
.on('progress', (count) => {
|
.on('progress', (count) => {
|
||||||
const max = step(n);
|
const max = step(n);
|
||||||
|
|
@ -68,3 +62,6 @@ function _loadFile(dir, n, file, callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showError({message}) {
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
|
|
|
||||||
44
client/get-json-from-file-table.js
Normal file
44
client/get-json-from-file-table.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* global DOM */
|
||||||
|
/**
|
||||||
|
* Функция генерирует JSON из html-таблицы файлов и
|
||||||
|
* используеться при первом заходе в корень
|
||||||
|
*/
|
||||||
|
export const getJsonFromFileTable = () => {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
|
const path = DOM.getCurrentDirPath();
|
||||||
|
const infoFiles = Info.files || [];
|
||||||
|
|
||||||
|
const files = infoFiles
|
||||||
|
.filter(notParent)
|
||||||
|
.map(parse);
|
||||||
|
|
||||||
|
const fileTable = {
|
||||||
|
path,
|
||||||
|
files,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fileTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
const notParent = (current) => {
|
||||||
|
const name = DOM.getCurrentName(current);
|
||||||
|
return name !== '..';
|
||||||
|
};
|
||||||
|
|
||||||
|
const parse = (current) => {
|
||||||
|
const name = DOM.getCurrentName(current);
|
||||||
|
const size = DOM.getCurrentSize(current);
|
||||||
|
const owner = DOM.getCurrentOwner(current);
|
||||||
|
const mode = DOM.getCurrentMode(current);
|
||||||
|
const date = DOM.getCurrentDate(current);
|
||||||
|
const type = DOM.getCurrentType(current);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
mode,
|
||||||
|
owner,
|
||||||
|
date,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const currify = require('currify/legacy');
|
|
||||||
|
|
||||||
const isType = currify((type, object, name) => {
|
|
||||||
return typeof object[name] === type;
|
|
||||||
});
|
|
||||||
|
|
||||||
const isBool = isType('boolean');
|
|
||||||
|
|
||||||
module.exports.getElementByName = getElementByName;
|
|
||||||
|
|
||||||
function getElementByName(selector, element) {
|
|
||||||
const str = `[data-name="js-${selector}"]`;
|
|
||||||
|
|
||||||
return element
|
|
||||||
.querySelector(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.getName = (element) => {
|
|
||||||
const name = element
|
|
||||||
.getAttribute('data-name')
|
|
||||||
.replace(/^js-/, '');
|
|
||||||
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.convert = (config) => {
|
|
||||||
const result = Object.assign({}, config);
|
|
||||||
const array = Object.keys(result);
|
|
||||||
|
|
||||||
array
|
|
||||||
.filter(isBool(result))
|
|
||||||
.forEach((name) => {
|
|
||||||
const item = result[name];
|
|
||||||
|
|
||||||
result[name] = setState(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
function setState(state) {
|
|
||||||
if (state)
|
|
||||||
return ' checked';
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.getValue = (name, element) => {
|
|
||||||
const el = getElementByName(name, element);
|
|
||||||
const type = el.type;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 'checkbox':
|
|
||||||
return el.checked;
|
|
||||||
|
|
||||||
case 'number':
|
|
||||||
return Number(el.value);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return el.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.setValue = (name, value, element) => {
|
|
||||||
const el = getElementByName(name, element);
|
|
||||||
const type = el.type;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 'checkbox':
|
|
||||||
el.checked = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
el.value = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
||||||
|
});
|
||||||
1001
client/key/index.js
1001
client/key/index.js
File diff suppressed because it is too large
Load diff
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,23 +1,20 @@
|
||||||
/* 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;
|
||||||
|
|
||||||
while(i < n && char === chars[i])
|
while (i < n && char === chars[i])
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (!i)
|
if (!i)
|
||||||
|
|
@ -31,19 +28,21 @@ 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;
|
|
||||||
} else {
|
|
||||||
if (skipN === skipCount)
|
|
||||||
firstByName = byName;
|
|
||||||
|
|
||||||
--skipCount;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipN === skipCount)
|
||||||
|
firstByName = byName;
|
||||||
|
|
||||||
|
--skipCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
names
|
names
|
||||||
|
|
@ -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,41 +1,36 @@
|
||||||
'use strict';
|
import {fullstore} from 'fullstore';
|
||||||
/* global DOM */
|
import limier from 'limier';
|
||||||
|
|
||||||
const fullstore = require('fullstore/legacy');
|
|
||||||
const limier = require('limier/legacy');
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
|
|
||||||
const searchStore = fullstore([]);
|
const searchStore = fullstore([]);
|
||||||
const searchIndex = fullstore(0);
|
const searchIndex = fullstore(0);
|
||||||
|
|
||||||
module.exports.find = (value) => {
|
export const find = (value, names) => {
|
||||||
const names = Info.files.map(DOM.getCurrentName);
|
|
||||||
const result = limier(value, names);
|
const result = limier(value, names);
|
||||||
|
|
||||||
searchStore(result);
|
searchStore(result);
|
||||||
searchIndex(0);
|
searchIndex(0);
|
||||||
|
|
||||||
DOM.setCurrentByName(result[0]);
|
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);
|
||||||
|
|
||||||
searchIndex(index);
|
searchIndex(index);
|
||||||
DOM.setCurrentByName(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);
|
||||||
|
|
||||||
searchIndex(index);
|
searchIndex(index);
|
||||||
DOM.setCurrentByName(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)
|
||||||
|
|
@ -50,4 +45,3 @@ function previous(index, length) {
|
||||||
|
|
||||||
return --index;
|
return --index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
55
client/key/vim/find.spec.js
Normal file
55
client/key/vim/find.spec.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import test from 'supertape';
|
||||||
|
import {getDOM} from './globals.fixture.js';
|
||||||
|
import {
|
||||||
|
_next,
|
||||||
|
_previous,
|
||||||
|
find,
|
||||||
|
findNext,
|
||||||
|
findPrevious,
|
||||||
|
} from './find.js';
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: _next', (t) => {
|
||||||
|
const result = _next(1, 2);
|
||||||
|
|
||||||
|
t.notOk(result, 'should return 0');
|
||||||
|
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) => {
|
||||||
|
const result = _previous(0, 2);
|
||||||
|
|
||||||
|
t.equal(result, 1, 'should return 1');
|
||||||
|
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,20 +1,19 @@
|
||||||
'use strict';
|
const noop = () => {};
|
||||||
|
|
||||||
module.exports.getDOM = () => {
|
export const getDOM = () => {
|
||||||
const resolve = 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Dialog = {
|
const Dialog = {
|
||||||
prompt: resolve
|
prompt,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -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,199 +1,167 @@
|
||||||
'use strict';
|
import vim from './vim.js';
|
||||||
/* global CloudCmd, DOM */
|
import * as finder from './find.js';
|
||||||
|
import {
|
||||||
|
setCurrent,
|
||||||
|
selectFileNotParent,
|
||||||
|
} from './set-current.js';
|
||||||
|
|
||||||
const KEY = require('../key');
|
export default (key, event, overrides = {}) => {
|
||||||
const Info = DOM.CurrentInfo;
|
const defaults = {
|
||||||
const Dialog = DOM.Dialog;
|
...globalThis.DOM,
|
||||||
|
...globalThis.CloudCmd,
|
||||||
const fullstore = require('fullstore/legacy');
|
};
|
||||||
const store = fullstore('');
|
|
||||||
const visual = fullstore(false);
|
const deps = {
|
||||||
const {
|
...defaults,
|
||||||
find,
|
...overrides,
|
||||||
findNext,
|
};
|
||||||
findPrevious,
|
|
||||||
} = require('./find');
|
const operations = getOperations(event, deps);
|
||||||
|
|
||||||
const TITLE = 'Cloud Commander';
|
vim(key, operations, deps);
|
||||||
|
|
||||||
const stopVisual = () => {
|
|
||||||
visual(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const end = () => {
|
const getOperations = (event, deps) => {
|
||||||
store('');
|
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 {
|
||||||
|
makeFile: createMakeFile({
|
||||||
|
promptNewFile,
|
||||||
|
preventDefault,
|
||||||
|
stopImmediatePropagation,
|
||||||
|
}),
|
||||||
|
findNext: createFindNext({
|
||||||
|
setCurrentByName,
|
||||||
|
}),
|
||||||
|
findPrevious: createFindPrevious({
|
||||||
|
setCurrentByName,
|
||||||
|
}),
|
||||||
|
escape: unselectFiles,
|
||||||
|
rename: () => {
|
||||||
|
event.preventDefault();
|
||||||
|
renameCurrent();
|
||||||
|
},
|
||||||
|
remove: () => {
|
||||||
|
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: () => {
|
||||||
|
Buffer.copy();
|
||||||
|
unselectFiles();
|
||||||
|
},
|
||||||
|
|
||||||
|
select: () => {
|
||||||
|
const current = Info.element;
|
||||||
|
toggleSelectedFile(current);
|
||||||
|
},
|
||||||
|
|
||||||
|
paste: Buffer.paste,
|
||||||
|
|
||||||
|
moveNext: ({count, isVisual, isDelete}) => {
|
||||||
|
setCurrent('next', {
|
||||||
|
count,
|
||||||
|
isVisual,
|
||||||
|
isDelete,
|
||||||
|
}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
movePrevious: ({count, isVisual, isDelete}) => {
|
||||||
|
setCurrent('previous', {
|
||||||
|
count,
|
||||||
|
isVisual,
|
||||||
|
isDelete,
|
||||||
|
}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
find: async () => {
|
||||||
|
preventDefault();
|
||||||
|
const [, value] = await prompt('Find', '');
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const names = Info.files.map(getCurrentName);
|
||||||
|
const [result] = finder.find(value, names);
|
||||||
|
|
||||||
|
setCurrentByName(result);
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const rmFirst = (a) => {
|
export const selectFile = selectFileNotParent;
|
||||||
return a
|
|
||||||
.split('')
|
const _createFindPrevious = (overrides = {}) => () => {
|
||||||
.slice(1)
|
const {setCurrentByName} = overrides;
|
||||||
.join('');
|
const name = finder.findPrevious();
|
||||||
|
|
||||||
|
setCurrentByName(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = (key, event) => {
|
const _createFindNext = (overrides = {}) => () => {
|
||||||
const current = Info.element;
|
const {setCurrentByName} = overrides;
|
||||||
const keyCode = event.keyCode;
|
const name = finder.findNext();
|
||||||
const prevStore = store();
|
|
||||||
|
|
||||||
const value = store(prevStore.concat(key));
|
setCurrentByName(name);
|
||||||
|
|
||||||
if (keyCode === KEY.ENTER)
|
|
||||||
return end();
|
|
||||||
|
|
||||||
if (keyCode === KEY.ESC) {
|
|
||||||
DOM.unselectFiles();
|
|
||||||
visual(false);
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'j') {
|
|
||||||
move('next', {
|
|
||||||
prevStore,
|
|
||||||
current,
|
|
||||||
});
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'k') {
|
|
||||||
move('previous', {
|
|
||||||
prevStore,
|
|
||||||
current,
|
|
||||||
});
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/gg/.test(value)) {
|
|
||||||
move('previous', {
|
|
||||||
current,
|
|
||||||
prevStore,
|
|
||||||
max: Infinity,
|
|
||||||
});
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'd' && (visual() || prevStore === 'd')) {
|
|
||||||
CloudCmd.Operation.show('delete');
|
|
||||||
stopVisual();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'G') {
|
|
||||||
move('next', {
|
|
||||||
current,
|
|
||||||
prevStore,
|
|
||||||
max: Infinity,
|
|
||||||
});
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'y') {
|
|
||||||
if (!visual())
|
|
||||||
return end();
|
|
||||||
|
|
||||||
DOM.Buffer.copy();
|
|
||||||
stopVisual();
|
|
||||||
DOM.unselectFiles();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^p$/i.test(key)) {
|
|
||||||
DOM.Buffer.paste();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^v$/i.test(key)) {
|
|
||||||
DOM.toggleSelectedFile(current);
|
|
||||||
visual(!visual());
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === '/') {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
Dialog.prompt(TITLE, 'Find', '', {cancel: false})
|
|
||||||
.then(find);
|
|
||||||
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'n') {
|
|
||||||
findNext();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'N') {
|
|
||||||
findPrevious();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.selectFile = selectFile;
|
const _createMakeFile = (overrides = {}) => () => {
|
||||||
|
const {
|
||||||
function move(sibling, {max, current, prevStore}) {
|
promptNewFile,
|
||||||
const isDelete = prevStore[0] === 'd';
|
stopImmediatePropagation,
|
||||||
|
preventDefault,
|
||||||
|
} = overrides;
|
||||||
|
|
||||||
if (isDelete) {
|
stopImmediatePropagation();
|
||||||
visual(true);
|
preventDefault();
|
||||||
prevStore = rmFirst(prevStore);
|
promptNewFile();
|
||||||
}
|
};
|
||||||
|
|
||||||
const n = max || getNumber(prevStore);
|
|
||||||
|
|
||||||
if (isNaN(n))
|
|
||||||
return;
|
|
||||||
|
|
||||||
setCurrent({
|
|
||||||
n,
|
|
||||||
current,
|
|
||||||
sibling,
|
|
||||||
visual: visual(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isDelete)
|
|
||||||
CloudCmd.Operation.show('delete');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNumber(value) {
|
|
||||||
if (!value)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (value === 'g')
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return parseInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectFile(current) {
|
|
||||||
const name = DOM.getCurrentName(current);
|
|
||||||
|
|
||||||
if (name === '..')
|
|
||||||
return;
|
|
||||||
|
|
||||||
DOM.selectFile(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCurrent({n, current, visual, sibling}) {
|
|
||||||
const select = visual ? selectFile : DOM.unselectFile;
|
|
||||||
|
|
||||||
select(current);
|
|
||||||
|
|
||||||
const position = `${sibling}Sibling`;
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
const next = current[position];
|
|
||||||
|
|
||||||
if (!next)
|
|
||||||
break;
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
select(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
DOM.setCurrentFile(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
756
client/key/vim/index.spec.js
Normal file
756
client/key/vim/index.spec.js
Normal file
|
|
@ -0,0 +1,756 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
globalThis.DOM = getDOM();
|
||||||
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const {assign} = Object;
|
||||||
|
const {DOM} = globalThis;
|
||||||
|
const {Buffer} = DOM;
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set next file: no', (t) => {
|
||||||
|
const element = {};
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('j', {}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [element], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set next file current: j', async (t) => {
|
||||||
|
const nextSibling = 'hello';
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
await vim('j', {}, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set next file current: mjj', (t) => {
|
||||||
|
const nextSibling = 'hello';
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('m', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set next file current: g', (t) => {
|
||||||
|
const nextSibling = 'hello';
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('g', {}, deps);
|
||||||
|
vim('j', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should ignore g');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set +2 file current', (t) => {
|
||||||
|
const last = {};
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
const element = {};
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
setCurrentFile,
|
||||||
|
Info,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('2', event, deps);
|
||||||
|
vim('j', event, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: select +2 files from current before delete', (t) => {
|
||||||
|
const last = {};
|
||||||
|
const nextSibling = {
|
||||||
|
nextSibling: last,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectFile = stub();
|
||||||
|
const getCurrentName = stub().returns('x');
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
Operation,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('d', event, deps);
|
||||||
|
vim('2', event, deps);
|
||||||
|
vim('j', event, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: delete +2 files from current', (t) => {
|
||||||
|
const last = {};
|
||||||
|
const nextSibling = {
|
||||||
|
nextSibling: last,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
const show = stub();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
Operation: {
|
||||||
|
show,
|
||||||
|
},
|
||||||
|
setCurrentFile,
|
||||||
|
selectFile: stub(),
|
||||||
|
getCurrentName: stub().returns('x'),
|
||||||
|
unselectFiles: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('d', event, deps);
|
||||||
|
vim('2', event, deps);
|
||||||
|
vim('j', event, deps);
|
||||||
|
|
||||||
|
t.calledWith(show, ['delete'], 'should call delete');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set previous file current', (t) => {
|
||||||
|
const previousSibling = 'hello';
|
||||||
|
const element = {
|
||||||
|
previousSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('k', {}, deps);
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [previousSibling], 'should set previous file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: copy: no', (t) => {
|
||||||
|
const copy = stub();
|
||||||
|
|
||||||
|
vim('y', {}, {
|
||||||
|
unselectFiles: stub(),
|
||||||
|
Buffer: {
|
||||||
|
copy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.notCalled(copy, 'should not copy files');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: copy', (t) => {
|
||||||
|
const copy = stub();
|
||||||
|
const Info = {
|
||||||
|
element: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelectedFile = stub();
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
toggleSelectedFile,
|
||||||
|
Buffer: {
|
||||||
|
copy,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('v', {}, deps);
|
||||||
|
vim('y', {}, deps);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(copy, 'should copy files');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
|
||||||
|
const unselectFiles = stub();
|
||||||
|
const Info = {
|
||||||
|
element: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelectedFile = stub();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
toggleSelectedFile,
|
||||||
|
Buffer: {
|
||||||
|
copy: stub(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('v', {}, deps);
|
||||||
|
vim('y', {}, deps);
|
||||||
|
|
||||||
|
t.calledWithNoArgs(unselectFiles, 'should unselect files');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: paste', (t) => {
|
||||||
|
const paste = stub();
|
||||||
|
|
||||||
|
Buffer.paste = paste;
|
||||||
|
|
||||||
|
vim('p', {}, {
|
||||||
|
Buffer,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(paste, 'should paste files');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: selectFile: ..', (t) => {
|
||||||
|
const getCurrentName = stub().returns('..');
|
||||||
|
const selectFile = stub();
|
||||||
|
const current = {};
|
||||||
|
|
||||||
|
vimSelectFile(current, {
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.notCalled(selectFile, 'should not call selectFile');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: selectFile', (t) => {
|
||||||
|
const selectFile = stub();
|
||||||
|
const getCurrentName = stub().returns('x');
|
||||||
|
const current = {};
|
||||||
|
|
||||||
|
vimSelectFile(current, {
|
||||||
|
selectFile,
|
||||||
|
getCurrentName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(selectFile, [current], 'should call selectFile');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: set last file current: shift + g', async (t) => {
|
||||||
|
const last = 'last';
|
||||||
|
const nextSibling = {
|
||||||
|
nextSibling: last,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
await vim('G', {}, {
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles: stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [last], 'should set last file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 previousSibling = {
|
||||||
|
previousSibling: first,
|
||||||
|
};
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
previousSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const unselectFiles = stub();
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
Operation,
|
||||||
|
unselectFiles,
|
||||||
|
setCurrentFile,
|
||||||
|
Info: {
|
||||||
|
element,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('g', {}, deps);
|
||||||
|
vim('g', {}, deps);
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: visual', (t) => {
|
||||||
|
const element = {};
|
||||||
|
const toggleSelectedFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('v', {}, {
|
||||||
|
Info,
|
||||||
|
toggleSelectedFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(toggleSelectedFile, [element], 'should toggle selection');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: ESC', (t) => {
|
||||||
|
const element = {};
|
||||||
|
const unselectFiles = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('Escape', null, {
|
||||||
|
Info,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(unselectFiles, 'should toggle selection');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: Enter', async (t) => {
|
||||||
|
const nextSibling = 'hello';
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unselectFiles = stub();
|
||||||
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
};
|
||||||
|
|
||||||
|
await vim('Enter', null, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
await vim('j', null, {
|
||||||
|
Info,
|
||||||
|
setCurrentFile,
|
||||||
|
unselectFiles,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: /', (t) => {
|
||||||
|
const preventDefault = stub();
|
||||||
|
const element = {};
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
element,
|
||||||
|
files: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentName = stub().returns('');
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
const prompt = stub().returns([]);
|
||||||
|
|
||||||
|
vim('/', event, {
|
||||||
|
getCurrentName,
|
||||||
|
Info,
|
||||||
|
prompt,
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: n', (t) => {
|
||||||
|
const findNext = stub();
|
||||||
|
const createFindNext = stub().returns(findNext);
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('n', event, {
|
||||||
|
createFindNext,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(findNext, 'should call findNext');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: N', (t) => {
|
||||||
|
const findPrevious = stub();
|
||||||
|
const createFindPrevious = stub().returns(findPrevious);
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('N', event, {
|
||||||
|
createFindPrevious,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWithNoArgs(findPrevious);
|
||||||
|
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');
|
||||||
|
};
|
||||||
220
client/key/vim/vim.js
Normal file
220
client/key/vim/vim.js
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
import {fullstore} from 'fullstore';
|
||||||
|
|
||||||
|
const store = fullstore('');
|
||||||
|
const visual = fullstore(false);
|
||||||
|
|
||||||
|
const stopVisual = () => {
|
||||||
|
visual(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const end = () => {
|
||||||
|
store('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const rmFirst = (a) => {
|
||||||
|
return a
|
||||||
|
.split('')
|
||||||
|
.slice(1)
|
||||||
|
.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
export default (key, operations = {}) => {
|
||||||
|
const prevStore = store();
|
||||||
|
const isVisual = visual();
|
||||||
|
const value = store(prevStore.concat(key));
|
||||||
|
|
||||||
|
const {
|
||||||
|
escape = noop,
|
||||||
|
moveNext = noop,
|
||||||
|
movePrevious = noop,
|
||||||
|
remove = noop,
|
||||||
|
copy = noop,
|
||||||
|
paste = noop,
|
||||||
|
select = noop,
|
||||||
|
find = noop,
|
||||||
|
findNext = noop,
|
||||||
|
findPrevious = noop,
|
||||||
|
makeFile = noop,
|
||||||
|
makeDirectory = noop,
|
||||||
|
terminal = noop,
|
||||||
|
edit = noop,
|
||||||
|
operationCopy = noop,
|
||||||
|
operationMove = noop,
|
||||||
|
rename = noop,
|
||||||
|
} = operations;
|
||||||
|
|
||||||
|
if (key === 'Enter')
|
||||||
|
return end();
|
||||||
|
|
||||||
|
if (key === 'Escape') {
|
||||||
|
visual(false);
|
||||||
|
escape();
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'j' || key === 'w') {
|
||||||
|
const {
|
||||||
|
count,
|
||||||
|
isDelete,
|
||||||
|
isVisual,
|
||||||
|
} = handleDelete(prevStore);
|
||||||
|
|
||||||
|
!isNaN(count) && moveNext({
|
||||||
|
count,
|
||||||
|
isVisual,
|
||||||
|
isDelete,
|
||||||
|
});
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'k' || key === 'b') {
|
||||||
|
const {
|
||||||
|
count,
|
||||||
|
isDelete,
|
||||||
|
isVisual,
|
||||||
|
} = handleDelete(prevStore);
|
||||||
|
|
||||||
|
!isNaN(count) && movePrevious({
|
||||||
|
count,
|
||||||
|
isVisual,
|
||||||
|
isDelete,
|
||||||
|
});
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'gg' || key === '^') {
|
||||||
|
const {isDelete, isVisual} = handleDelete(prevStore);
|
||||||
|
|
||||||
|
movePrevious({
|
||||||
|
count: Infinity,
|
||||||
|
isVisual,
|
||||||
|
isDelete,
|
||||||
|
});
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'md') {
|
||||||
|
makeDirectory();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
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({
|
||||||
|
count: Infinity,
|
||||||
|
isVisual,
|
||||||
|
});
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'y') {
|
||||||
|
if (!visual())
|
||||||
|
return end();
|
||||||
|
|
||||||
|
stopVisual();
|
||||||
|
copy();
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^p$/i.test(key)) {
|
||||||
|
paste();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^v$/i.test(key)) {
|
||||||
|
visual(!visual());
|
||||||
|
select();
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === '/') {
|
||||||
|
find();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'n') {
|
||||||
|
findNext();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'N') {
|
||||||
|
findPrevious();
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === ' ')
|
||||||
|
return end();
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleDelete(prevStore) {
|
||||||
|
const isDelete = prevStore[0] === 'd';
|
||||||
|
|
||||||
|
if (isDelete) {
|
||||||
|
visual(true);
|
||||||
|
prevStore = rmFirst(prevStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = getNumber(prevStore);
|
||||||
|
const isVisual = visual();
|
||||||
|
|
||||||
|
return {
|
||||||
|
count,
|
||||||
|
isDelete,
|
||||||
|
isVisual,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNumber(value) {
|
||||||
|
if (!value)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (value === 'g')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return parseInt(value);
|
||||||
|
}
|
||||||
100
client/key/vim/vim.spec.js
Normal file
100
client/key/vim/vim.spec.js
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
import {test, stub} from 'supertape';
|
||||||
|
import vim from './vim.js';
|
||||||
|
|
||||||
|
test('vim: no operations', (t) => {
|
||||||
|
const result = vim('hello', {});
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
|
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('tape');
|
|
||||||
|
|
||||||
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,38 +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/legacy');
|
import clipboard from '@cloudcmd/clipboard';
|
||||||
const currify = require('currify/legacy');
|
import * as Events from '#dom/events';
|
||||||
const getRange = require('./get-range');
|
import {uploadFiles} from '#dom/upload-files';
|
||||||
|
import {FS} from '#common/cloudfunc';
|
||||||
const getIndex = currify(require('./get-index'));
|
import {getRange} from './get-range.js';
|
||||||
|
import {getIndex} from './get-index.js';
|
||||||
const uploadFiles = require('../dom/upload-files');
|
|
||||||
|
|
||||||
const {
|
|
||||||
FS,
|
|
||||||
apiURL
|
|
||||||
} = 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,
|
||||||
|
|
@ -46,23 +38,16 @@ const unselect = (event) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const execAll = currify((funcs, event) => {
|
const execAll = currify((funcs, event) => {
|
||||||
funcs.forEach((fn) => {
|
for (const fn of funcs)
|
||||||
fn(event);
|
fn(event);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const Storage = DOM.Storage;
|
|
||||||
const Events = DOM.Events;
|
|
||||||
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),
|
||||||
};
|
};
|
||||||
|
|
||||||
let EXT;
|
let EXT;
|
||||||
|
|
@ -70,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);
|
||||||
};
|
};
|
||||||
|
|
@ -81,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;
|
||||||
|
|
@ -103,44 +88,48 @@ function getPath(el, path = []) {
|
||||||
return getPath(el.parentElement, path.concat(el));
|
return getPath(el.parentElement, path.concat(el));
|
||||||
}
|
}
|
||||||
|
|
||||||
function config() {
|
async function config() {
|
||||||
DOM.Files.get('config', (e, 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 id = target.id;
|
const {target} = event;
|
||||||
|
const {id} = target;
|
||||||
|
|
||||||
const operation = (name) => {
|
const operation = (name) => {
|
||||||
const Operation = CloudCmd.Operation;
|
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' : DOM.renameCurrent,
|
'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': () => {
|
||||||
'f10' : CloudCmd.Config.show,
|
event.stopPropagation();
|
||||||
'~' : CloudCmd.Konsole.show,
|
CloudCmd.Menu.show();
|
||||||
'shift~' : CloudCmd.Terminal.show,
|
},
|
||||||
'contact' : CloudCmd.Contact.show,
|
'f10': CloudCmd.Config.show,
|
||||||
|
'~': CloudCmd.Konsole.show,
|
||||||
|
'shift~': CloudCmd.Terminal.show,
|
||||||
|
'contact': CloudCmd.Contact.show,
|
||||||
};
|
};
|
||||||
|
|
||||||
exec(clickFuncs[id]);
|
exec(clickFuncs[id]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -149,10 +138,10 @@ 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,41 +157,45 @@ function getPathListener(panel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNoCurrent(panel) {
|
function isNoCurrent(panel) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
const infoPanel = Info.panel;
|
const infoPanel = Info.panel;
|
||||||
|
|
||||||
|
if (!infoPanel)
|
||||||
|
return true;
|
||||||
|
|
||||||
const namePanel = panel.getAttribute('data-name');
|
const namePanel = panel.getAttribute('data-name');
|
||||||
const nameInfoPanel = infoPanel.getAttribute('data-name');
|
const nameInfoPanel = infoPanel.getAttribute('data-name');
|
||||||
|
|
||||||
return namePanel !== nameInfoPanel;
|
return namePanel !== nameInfoPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodePath(path){
|
function decodePath(path) {
|
||||||
const url = CloudCmd.HOST;
|
const url = CloudCmd.HOST;
|
||||||
const prefix = CloudCmd.PREFIX;
|
const {prefix} = CloudCmd;
|
||||||
const prefixReg = RegExp('^' + prefix + FS);
|
const prefixReg = RegExp('^' + prefix + FS);
|
||||||
|
|
||||||
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) || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
const attr = element.getAttribute('data-name');
|
const attr = element.getAttribute('data-name');
|
||||||
const noCurrent = isNoCurrent(panel);
|
const noCurrent = isNoCurrent(panel);
|
||||||
|
|
||||||
if (attr === 'js-clear-storage')
|
if (attr === 'js-copy-path')
|
||||||
return Storage.clear();
|
return copyPath(element);
|
||||||
|
|
||||||
if (attr === 'js-refresh')
|
if (attr === 'js-refresh')
|
||||||
return CloudCmd.refresh({
|
return CloudCmd.refresh({
|
||||||
panel,
|
panel,
|
||||||
noCurrent
|
noCurrent,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (attr !== 'js-path-link')
|
if (attr !== 'js-path-link')
|
||||||
|
|
@ -211,13 +204,26 @@ function onPathElementClick(panel, event) {
|
||||||
const {href} = element;
|
const {href} = element;
|
||||||
const path = decodePath(href);
|
const path = decodePath(href);
|
||||||
|
|
||||||
CloudCmd.loadDir({
|
await CloudCmd.changeDir(path, {
|
||||||
path,
|
|
||||||
isRefresh: false,
|
isRefresh: false,
|
||||||
panel: noCurrent ? panel : Info.panel
|
panel: noCurrent ? panel : Info.panel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyPath(el) {
|
||||||
|
clipboard
|
||||||
|
.writeText(el.parentElement.title)
|
||||||
|
.then(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;
|
||||||
|
|
@ -227,30 +233,27 @@ function execIfNotUL(callback, event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(event) {
|
function onClick(event) {
|
||||||
const ctrl = event.ctrlKey;
|
event.preventDefault();
|
||||||
|
|
||||||
if (!ctrl)
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
changePanel(event.target);
|
changePanel(event.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
return files.forEach(DOM.selectFile);
|
return files.map(DOM.selectFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -259,21 +262,20 @@ function changePanel(element) {
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
CloudCmd.loadDir({
|
return CloudCmd.View.show();
|
||||||
path: path === '/' ? '/' : path + '/'
|
|
||||||
});
|
await CloudCmd.changeDir(path);
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouch(event) {
|
async function onTouch(event) {
|
||||||
const current = getLIElement(event.target);
|
const current = getLIElement(event.target);
|
||||||
const isDir = DOM.isCurrentIsDir(current);
|
const isDir = DOM.isCurrentIsDir(current);
|
||||||
|
|
||||||
|
|
@ -285,11 +287,7 @@ function onTouch(event) {
|
||||||
if (!isCurrent)
|
if (!isCurrent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CloudCmd.loadDir({
|
await CloudCmd.changeDir(DOM.getCurrentPath(current));
|
||||||
path: DOM.getCurrentPath(current)
|
|
||||||
});
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -297,8 +295,10 @@ function onTouch(event) {
|
||||||
* in Chrome (HTML5)
|
* in Chrome (HTML5)
|
||||||
*/
|
*/
|
||||||
function onDragStart(event) {
|
function onDragStart(event) {
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
|
const {prefixURL} = CloudCmd;
|
||||||
const element = getLIElement(event.target);
|
const element = getLIElement(event.target);
|
||||||
const isDir = Info.isDir;
|
const {isDir} = Info;
|
||||||
let link = DOM.getCurrentLink(element);
|
let link = DOM.getCurrentLink(element);
|
||||||
let name = DOM.getCurrentName(element);
|
let name = DOM.getCurrentName(element);
|
||||||
|
|
||||||
|
|
@ -307,13 +307,15 @@ function onDragStart(event) {
|
||||||
name += EXT;
|
name += EXT;
|
||||||
link = document.createElement('a');
|
link = document.createElement('a');
|
||||||
link.textContent = name;
|
link.textContent = name;
|
||||||
link.href = apiURL + '/pack' + Info.path + EXT;
|
link.href = prefixURL + '/pack' + Info.path + EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.dataTransfer.setData('DownloadURL',
|
event.dataTransfer.setData(
|
||||||
'application/octet-stream' + ':' +
|
'DownloadURL',
|
||||||
name + ':' +
|
'application/octet-stream' + ':' + name +
|
||||||
link);
|
':' +
|
||||||
|
link,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLIElement(element) {
|
function getLIElement(element) {
|
||||||
|
|
@ -334,13 +336,14 @@ 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 = {
|
||||||
alt: event.altKey,
|
alt: event.altKey,
|
||||||
ctrl: event.ctrlKey,
|
ctrl: event.ctrlKey,
|
||||||
meta: event.metaKey,
|
meta: event.metaKey,
|
||||||
shift: event.shiftKey
|
shift: event.shiftKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = getLIElement(event.target);
|
const element = getLIElement(event.target);
|
||||||
|
|
@ -377,7 +380,7 @@ function contextMenu() {
|
||||||
Events.addOnce('contextmenu', fm, (event) => {
|
Events.addOnce('contextmenu', fm, (event) => {
|
||||||
CloudCmd.Menu.show({
|
CloudCmd.Menu.show({
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
y: event.clientY
|
y: event.clientY,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -388,21 +391,17 @@ function contextMenu() {
|
||||||
|
|
||||||
function dragndrop() {
|
function dragndrop() {
|
||||||
const panels = DOM.getByClassAll('panel');
|
const panels = DOM.getByClassAll('panel');
|
||||||
const select = () => {
|
const select = ({target}) => {
|
||||||
[...panels].forEach((panel) => {
|
target.classList.add('selected-panel');
|
||||||
panel.classList.add('selected-panel');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const unselect = () => {
|
const unselect = ({target}) => {
|
||||||
[...panels].forEach((panel) => {
|
target.classList.remove('selected-panel');
|
||||||
panel.classList.remove('selected-panel');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDrop = (event) => {
|
const onDrop = (event) => {
|
||||||
const files = event.dataTransfer.files;
|
const {files, items} = event.dataTransfer;
|
||||||
const items = event.dataTransfer.items;
|
|
||||||
const {length: filesCount} = files;
|
const {length: filesCount} = files;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -411,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 = [...items].filter(isFile);
|
|
||||||
|
const dirFiles = Array
|
||||||
|
.from(items)
|
||||||
|
.filter(isFile);
|
||||||
|
|
||||||
if (dirFiles.length)
|
if (dirFiles.length)
|
||||||
return DOM.uploadDirectory(dirFiles);
|
return DOM.uploadDirectory(dirFiles);
|
||||||
|
|
@ -423,13 +425,13 @@ 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
|
||||||
*/
|
*/
|
||||||
const onDragOver = (event) => {
|
const onDragOver = (event) => {
|
||||||
const dataTransfer = event.dataTransfer;
|
const {dataTransfer} = event;
|
||||||
const effectAllowed = dataTransfer.effectAllowed;
|
const {effectAllowed} = dataTransfer;
|
||||||
|
|
||||||
if (/move|linkMove/.test(effectAllowed))
|
if (/move|linkMove/.test(effectAllowed))
|
||||||
dataTransfer.dropEffect = 'move';
|
dataTransfer.dropEffect = 'move';
|
||||||
|
|
@ -439,19 +441,18 @@ function dragndrop() {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
Events.add('dragenter', select);
|
for (const panel of panels)
|
||||||
Events.add(['dragleave', 'drop'], unselect);
|
Events
|
||||||
|
.add('dragover', panel, onDragOver)
|
||||||
[...panels].forEach((panel) => {
|
.add('drop', panel, onDrop)
|
||||||
Events.add('dragover', panel, onDragOver)
|
.add('dragenter', select)
|
||||||
.add('drop', panel, onDrop);
|
.add(['dragleave', 'drop'], unselect);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function unload() {
|
function unload() {
|
||||||
DOM.Events.add(['unload', 'beforeunload'], (event) => {
|
Events.add(['unload', 'beforeunload'], (event) => {
|
||||||
const Key = CloudCmd.Key;
|
const {Key} = CloudCmd;
|
||||||
const isBind = Key && Key.isBind();
|
const isBind = Key?.isBind();
|
||||||
|
|
||||||
if (isBind)
|
if (isBind)
|
||||||
return;
|
return;
|
||||||
|
|
@ -462,15 +463,15 @@ function unload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function pop() {
|
function pop() {
|
||||||
Events.add('popstate', ({state}) => {
|
Events.add('popstate', async ({state}) => {
|
||||||
const path = (state || '').replace(FS, '');
|
const path = (state || '').replace(FS, '');
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
return CloudCmd.route(location.hash);
|
return CloudCmd.route(location.hash);
|
||||||
|
|
||||||
const history = false;
|
const history = false;
|
||||||
CloudCmd.loadDir({
|
|
||||||
path,
|
await CloudCmd.changeDir(path, {
|
||||||
history,
|
history,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -478,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;
|
||||||
|
|
@ -498,4 +500,3 @@ function resize() {
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
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