Compare commits
626 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caed1a2f3c | ||
|
|
57f932851a | ||
|
|
cc956edf2d | ||
|
|
10f3a7193a | ||
|
|
f08865eedc | ||
|
|
cf67df3bf9 | ||
|
|
86c14a1d78 | ||
|
|
6411901200 | ||
|
|
af110337ce | ||
|
|
789b934996 | ||
|
|
9ec5c91a8d | ||
|
|
f89a901f6b | ||
|
|
063496e738 | ||
|
|
847fb4d52c | ||
|
|
3157de7e8b | ||
|
|
3cd0eaaf58 | ||
|
|
184aa59799 | ||
|
|
b6e88e713d | ||
|
|
09eabbf848 | ||
|
|
84d0e5c39e | ||
|
|
c63caa6015 | ||
|
|
b9cf079a85 | ||
|
|
ce4059a68e | ||
|
|
65a4ca8925 | ||
|
|
2a049433eb | ||
|
|
89ca42dabd | ||
|
|
12f6a324b9 | ||
|
|
f3d2ad55f6 | ||
|
|
62a911161d | ||
|
|
f4adfc4757 | ||
|
|
2de0dfb81c | ||
|
|
abe95a0876 | ||
|
|
0f0baf904f | ||
|
|
ef0eb6b047 | ||
|
|
323dd6b6f4 | ||
|
|
7edf8e4f3f | ||
|
|
b76980de14 | ||
|
|
a13b4bc514 | ||
|
|
cf58ee1d1f | ||
|
|
f7774f7ed0 | ||
|
|
8417e925a9 | ||
|
|
068d6dff6e | ||
|
|
f96dac147f | ||
|
|
9b212fa9e7 | ||
|
|
185e6093ed | ||
|
|
999499b840 | ||
|
|
0279af9f57 | ||
|
|
8305f1a8cb | ||
|
|
39019f03ed | ||
|
|
e0d4312cbe | ||
|
|
c0a364ef57 | ||
|
|
b1e5c3b361 | ||
|
|
fa9e955fc2 | ||
|
|
0504c35f2b | ||
|
|
4b8882f222 | ||
|
|
35e1a8e821 | ||
|
|
35be891910 | ||
|
|
2400556da3 | ||
|
|
09d3d26b45 | ||
|
|
a51fd1d35e | ||
|
|
dd4b388452 | ||
|
|
f6eb1e9652 | ||
|
|
6ab7a92184 | ||
|
|
5523cece62 | ||
|
|
3198f6172b | ||
|
|
ae2d07838c | ||
|
|
c81394fe66 | ||
|
|
389f9366e0 | ||
|
|
9921133d14 | ||
|
|
f751457313 | ||
|
|
33fe4d51e7 | ||
|
|
0d0b20e60e | ||
|
|
7e99be469d | ||
|
|
acdb26d476 | ||
|
|
7cc7e56f73 | ||
|
|
18bbe98cc6 | ||
|
|
3f042d71f2 | ||
|
|
5db9ca9786 | ||
|
|
b95eaf1aed | ||
|
|
9023e62fb4 | ||
|
|
45bab0cc00 | ||
|
|
70fb0c1c6b | ||
|
|
95ee8d101f | ||
|
|
fa91c4b4d5 | ||
|
|
2108cb8e1c | ||
|
|
e5d9a25cff | ||
|
|
c06cb8d9a7 | ||
|
|
da0f5f2bd1 | ||
|
|
8e9af90289 | ||
|
|
3f66d87a8a | ||
|
|
c019ef0a24 | ||
|
|
eac2e1548a | ||
|
|
2934a5517d | ||
|
|
4e1d3e9fe9 | ||
|
|
47e88ff055 | ||
|
|
9ee398af19 | ||
|
|
e369791f37 | ||
|
|
986abd5549 | ||
|
|
e61ec3e576 | ||
|
|
430fde9ea9 | ||
|
|
ad1c7450ab | ||
|
|
02f28eb87c | ||
|
|
b2645c50e7 | ||
|
|
1bf17d5514 | ||
|
|
36fb51705b | ||
|
|
668f99c37f | ||
|
|
a53c7ce08c | ||
|
|
1068f8a664 | ||
|
|
8c98e7fcd7 | ||
|
|
282749166a | ||
|
|
1f3d1fd344 | ||
|
|
1250a46274 | ||
|
|
e7a7c7b5ac | ||
|
|
be00a6fd60 | ||
|
|
0647fc3297 | ||
|
|
6c28ebe057 | ||
|
|
db0826d43a | ||
|
|
b1157a0615 | ||
|
|
e9830479af | ||
|
|
55cacc2a81 | ||
|
|
201fe59983 | ||
|
|
22a50571e6 | ||
|
|
1245ecf8fb | ||
|
|
312d4c1721 | ||
|
|
236a38f6c9 | ||
|
|
df8da24b5a | ||
|
|
9c28042fdd | ||
|
|
91310a803c | ||
|
|
a4aeb83a01 | ||
|
|
fc82976cf0 | ||
|
|
17adfeb553 | ||
|
|
41c82e1ea9 | ||
|
|
f51bcc2406 | ||
|
|
84eda99dc0 | ||
|
|
fa268fd092 | ||
|
|
9103ceeb1e | ||
|
|
224079674b | ||
|
|
2f13546b8b | ||
|
|
2ac8a45446 | ||
|
|
0673cf0bbb | ||
|
|
55dfb12358 | ||
|
|
27d3e9e6b7 | ||
|
|
a09d7d76d3 | ||
|
|
96adfa0b5a | ||
|
|
4ee7ff45e3 | ||
|
|
8334441f2d | ||
|
|
36ec17a06c | ||
|
|
2f47fc534d | ||
|
|
bd64e4a45b | ||
|
|
3743ff96ff | ||
|
|
932b92e67d | ||
|
|
770556aa74 | ||
|
|
26bc275f0f | ||
|
|
ae8dccf292 | ||
|
|
9e9a73efc6 | ||
|
|
94364f0dad | ||
|
|
635c82c316 | ||
|
|
e2bae8a61f | ||
|
|
93300a4932 | ||
|
|
613f3c5cbc | ||
|
|
04f55cd9c4 | ||
|
|
7f7d509212 | ||
|
|
cbbc0e5efa | ||
|
|
3208cec882 | ||
|
|
4b3cee7f43 | ||
|
|
705fd9971f | ||
|
|
ee58075d3f | ||
|
|
7ad6cd83db | ||
|
|
3f5307ef79 | ||
|
|
b244ee8580 | ||
|
|
16bcb65d94 | ||
|
|
1b9db42114 | ||
|
|
de63ff558d | ||
|
|
307daf2a7a | ||
|
|
5e551b4ffe | ||
|
|
f6316ca956 | ||
|
|
4d764b6bac | ||
|
|
1070260476 | ||
|
|
f839189121 | ||
|
|
a73d8053b1 | ||
|
|
395029489a | ||
|
|
1c99ffa3cc | ||
|
|
18178741c8 | ||
|
|
6bf9fc29e7 | ||
|
|
299d8bbc90 | ||
|
|
7c37123216 | ||
|
|
ab06ee6bd4 | ||
|
|
719928f5e5 | ||
|
|
0d55dc2b38 | ||
|
|
b229bf6197 | ||
|
|
35b47b83d7 | ||
|
|
85d6c3ac34 | ||
|
|
5847f0b16e | ||
|
|
cad907125b | ||
|
|
d989da2811 | ||
|
|
ed53b433b5 | ||
|
|
18a0c391f6 | ||
|
|
d78c4041a3 | ||
|
|
c62b65b012 | ||
|
|
6a9d026052 | ||
|
|
f562dc9e2a | ||
|
|
0fdb4197a1 | ||
|
|
39cbe8a858 | ||
|
|
ac29f7eeb0 | ||
|
|
6de37b5ed9 | ||
|
|
35163086f9 | ||
|
|
14ce62ed50 | ||
|
|
81736b77a3 | ||
|
|
26bb639b67 | ||
|
|
d56401c347 | ||
|
|
1213cf3140 | ||
|
|
e70e448b77 | ||
|
|
f0bff83fa0 | ||
|
|
249af016a0 | ||
|
|
c8a480b0e3 | ||
|
|
ccd96e67b1 | ||
|
|
9317b5b983 | ||
|
|
11e6a8838c | ||
|
|
82ad9cbf5e | ||
|
|
21386b924e | ||
|
|
87087c7eac | ||
|
|
8ccbe56282 | ||
|
|
6e5e974767 | ||
|
|
7c0987dc88 | ||
|
|
2f16882c1b | ||
|
|
0377232e1e | ||
|
|
51dd5c5900 | ||
|
|
4f75e5d631 | ||
|
|
3d116decbe | ||
|
|
12317d9649 | ||
|
|
ade7733d47 | ||
|
|
3ad814ebb7 | ||
|
|
f5798438ac | ||
|
|
cf21a20f05 | ||
|
|
13784db34d | ||
|
|
2fced1dd60 | ||
|
|
c54b019d55 | ||
|
|
fe1034ce1a | ||
|
|
2068556ca5 | ||
|
|
55d1283320 | ||
|
|
65aeddd717 | ||
|
|
6e24ee30e1 | ||
|
|
42394c9c56 | ||
|
|
e5b72c7072 | ||
|
|
befb151ea4 | ||
|
|
efbe5f946e | ||
|
|
c994b7232a | ||
|
|
6008e3da03 | ||
|
|
2d321bc27d | ||
|
|
0afd0205ed | ||
|
|
78aae759e4 | ||
|
|
b6b3216f55 | ||
|
|
bb671547d6 | ||
|
|
b561f6c8ae | ||
|
|
bc2a723e80 | ||
|
|
b506e45bee | ||
|
|
7df140d226 | ||
|
|
271008d748 | ||
|
|
d5759286fa | ||
|
|
7fc58666ad | ||
|
|
a95666ab1d | ||
|
|
be1d94909b | ||
|
|
a7442e9623 | ||
|
|
abfaf24627 | ||
|
|
a36b4207c8 | ||
|
|
13f6cfd601 | ||
|
|
75b69cad21 | ||
|
|
26326d8878 | ||
|
|
612b518198 | ||
|
|
6f04d9e8c0 | ||
|
|
940d2a8b45 | ||
|
|
4c3a6c0952 | ||
|
|
e4381c974f | ||
|
|
393a02f98b | ||
|
|
855a3925fa | ||
|
|
14274b20fd | ||
|
|
351e694448 | ||
|
|
dc3b069c23 | ||
|
|
60bae8253f | ||
|
|
30ad96322d | ||
|
|
079c84f2df | ||
|
|
50badac708 | ||
|
|
fdb9a351f4 | ||
|
|
b106126565 | ||
|
|
51823b1a96 | ||
|
|
6d0c18fce3 | ||
|
|
51c500fd83 | ||
|
|
9481587151 | ||
|
|
e6387c694e | ||
|
|
53f3af6944 | ||
|
|
901162c8b6 | ||
|
|
13be809cfe | ||
|
|
8857821716 | ||
|
|
0c873a1c02 | ||
|
|
74d2119f31 | ||
|
|
20a8296cd5 | ||
|
|
f42e0329da | ||
|
|
918ef57c78 | ||
|
|
1b93ece0de | ||
|
|
4bb6981cbb | ||
|
|
2878207170 | ||
|
|
ad41d265b3 | ||
|
|
df8d176a34 | ||
|
|
e764df0687 | ||
|
|
182590ef92 | ||
|
|
fdf0d63665 | ||
|
|
3ecc170fd2 | ||
|
|
d1a2786c7c | ||
|
|
e4db91e35b | ||
|
|
b861c4a7b4 | ||
|
|
ddb86da459 | ||
|
|
ac36618de8 | ||
|
|
8dadec6220 | ||
|
|
dd4402fca5 | ||
|
|
00e0e797b0 | ||
|
|
5038551165 | ||
|
|
1d677a7ea5 | ||
|
|
3781445e5d | ||
|
|
e1e060859b | ||
|
|
0cc59898b1 | ||
|
|
1660373153 | ||
|
|
a5b0626792 | ||
|
|
a0fa933430 | ||
|
|
43e8f31f6a | ||
|
|
42411038e4 | ||
|
|
164624786d | ||
|
|
945da8bf92 | ||
|
|
95eb7b86c9 | ||
|
|
e34662baec | ||
|
|
d93c76373c | ||
|
|
3fa9c22a04 | ||
|
|
2cde1a6e02 | ||
|
|
61b38b4405 | ||
|
|
5ed3650f7b | ||
|
|
b7ac58b31a | ||
|
|
c86d4e8060 | ||
|
|
cb2240c71d | ||
|
|
d4bffba010 | ||
|
|
aef118880a | ||
|
|
2f39f7f68b | ||
|
|
d67e22e57b | ||
|
|
96750cd04f | ||
|
|
5960aea37a | ||
|
|
a7b70cc947 | ||
|
|
54d9897048 | ||
|
|
296f31e7a1 | ||
|
|
0ad76be012 | ||
|
|
d1b4d135c8 | ||
|
|
268748f85f | ||
|
|
28068fb5d5 | ||
|
|
c0c7036ada | ||
|
|
68ce66e881 | ||
|
|
9b010ee633 | ||
|
|
8cf80c7bbf | ||
|
|
1038b4b349 | ||
|
|
a993e21387 | ||
|
|
46ee372c0f | ||
|
|
afd466b516 | ||
|
|
b6051d6fc2 | ||
|
|
a23c3424ba | ||
|
|
5ae371db18 | ||
|
|
0c255bae22 | ||
|
|
464d550f3b | ||
|
|
c86dd978b5 | ||
|
|
8a4f9ed33e | ||
|
|
039fbcd809 | ||
|
|
7a8d629a81 | ||
|
|
0816fc6682 | ||
|
|
163be1f1b5 | ||
|
|
f246df572b | ||
|
|
a3463a6ac1 | ||
|
|
265ec57efe | ||
|
|
c2b284175f | ||
|
|
c33d7e9ada | ||
|
|
755e758f75 | ||
|
|
5d1c13ce2b | ||
|
|
25bc37294b | ||
|
|
ec7a45db92 | ||
|
|
f2837b1b2d | ||
|
|
e95a4a9782 | ||
|
|
0a91fb66e3 | ||
|
|
5e71831019 | ||
|
|
e0d66a2346 | ||
|
|
5602d2fd62 | ||
|
|
6545a9343d | ||
|
|
0350355626 | ||
|
|
0aec1e1607 | ||
|
|
077cb583b6 | ||
|
|
c62e368cee | ||
|
|
cb1f366d35 | ||
|
|
acba29add9 | ||
|
|
29b0f3b1bf | ||
|
|
15c564fc61 | ||
|
|
17639e94da | ||
|
|
928545e7eb | ||
|
|
80a0669c69 | ||
|
|
7aee2f4211 | ||
|
|
a43ec422c0 | ||
|
|
4147bac094 | ||
|
|
16d9e1e34a | ||
|
|
7645d0d2c9 | ||
|
|
db31dbeb0e | ||
|
|
a84eac06d3 | ||
|
|
41e6f7d61e | ||
|
|
aa1d3c6943 | ||
|
|
f3fa8d4e73 | ||
|
|
55816acc99 | ||
|
|
8226d0a1ae | ||
|
|
4d4a55cd3a | ||
|
|
dd5dfdbabd | ||
|
|
7c49f58565 | ||
|
|
32218cff9f | ||
|
|
0b5b342542 | ||
|
|
19a57e8ecf | ||
|
|
a696c33bca | ||
|
|
b6a759ab29 | ||
|
|
a3acf34b39 | ||
|
|
3d1b4f0ee2 | ||
|
|
aa1cb313b8 | ||
|
|
1c5ee17d00 | ||
|
|
de3a409a1c | ||
|
|
19a4156b1c | ||
|
|
aad76bc9d9 | ||
|
|
25f94341ad | ||
|
|
cafead69f6 | ||
|
|
a359ebae02 | ||
|
|
c1abf5f07c | ||
|
|
fc5d98c05c | ||
|
|
9eb9365c89 | ||
|
|
be4cb4efc8 | ||
|
|
0c543908d3 | ||
|
|
3786ef90fe | ||
|
|
6ca847bdde | ||
|
|
bc43291a0c | ||
|
|
3b285f088f | ||
|
|
9e26882b5f | ||
|
|
a038b59ef4 | ||
|
|
ad073427b3 | ||
|
|
6bceab7488 | ||
|
|
3d617811b5 | ||
|
|
daa491cd17 | ||
|
|
4b4e6ca31a | ||
|
|
0c9977a92a | ||
|
|
8d90875e18 | ||
|
|
09872f6b26 | ||
|
|
6f0698bae2 | ||
|
|
414d24262b | ||
|
|
e424128a60 | ||
|
|
23ca857154 | ||
|
|
542b912090 | ||
|
|
7750a7bd58 | ||
|
|
2514a60fb3 | ||
|
|
8c8d4a129e | ||
|
|
a3f8abba1a | ||
|
|
e0dc8ea46d | ||
|
|
bc91487388 | ||
|
|
cc971e7e6d | ||
|
|
f485af3e21 | ||
|
|
edc8259b6f | ||
|
|
ca568cd3eb | ||
|
|
bac9dd1eec | ||
|
|
bb76c6c3da | ||
|
|
d71a4ba0bb | ||
|
|
4237dfc9d1 | ||
|
|
dc6ac391d7 | ||
|
|
d96b734639 | ||
|
|
4796fe4024 | ||
|
|
f37aedc3b2 | ||
|
|
5ec993f9f4 | ||
|
|
11791deec5 | ||
|
|
0861a7f303 | ||
|
|
dac6862b31 | ||
|
|
51bde261e5 | ||
|
|
7c9188ed03 | ||
|
|
090f72eedb | ||
|
|
780c35e804 | ||
|
|
f037a0eb17 | ||
|
|
834850fd98 | ||
|
|
e632b04b16 | ||
|
|
a7058a9e05 | ||
|
|
6bf8a6cbb3 | ||
|
|
acedaf27d9 | ||
|
|
9868a7d31e | ||
|
|
af51c48ede | ||
|
|
b5f812512f | ||
|
|
df2ceb3fe9 | ||
|
|
b1588d4553 | ||
|
|
f004a9c322 | ||
|
|
1652381444 | ||
|
|
ae2d996754 | ||
|
|
0a4839eaf7 | ||
|
|
85d51b2362 | ||
|
|
1a38f519f4 | ||
|
|
7bd87c4113 | ||
|
|
38f6f4ef2e | ||
|
|
8912004dcc | ||
|
|
be044dbcce | ||
|
|
2d89b6fc27 | ||
|
|
f265046676 | ||
|
|
515ea7e83d | ||
|
|
36a07290f2 | ||
|
|
0d0ec37edf | ||
|
|
66268b194c | ||
|
|
0377013d47 | ||
|
|
30a42907b5 | ||
|
|
52431e67a4 | ||
|
|
dabb7c370a | ||
|
|
71393a3cf7 | ||
|
|
c31a758435 | ||
|
|
0411b205ff | ||
|
|
8a754a5c89 | ||
|
|
accc439d75 | ||
|
|
271bd06176 | ||
|
|
872d2c09cd | ||
|
|
b4bc7cb709 | ||
|
|
e1889c1db0 | ||
|
|
997c095f35 | ||
|
|
6f7fd2fc7b | ||
|
|
2b1a73e04d | ||
|
|
8403b9c409 | ||
|
|
40c27f5139 | ||
|
|
6bed41b8c1 | ||
|
|
3cd8d94ceb | ||
|
|
c23fb7bfb2 | ||
|
|
54876e6db3 | ||
|
|
b46b2c1e8e | ||
|
|
bfffdbc4d8 | ||
|
|
fddd41f6b4 | ||
|
|
8759b245ea | ||
|
|
1a89a1b5f5 | ||
|
|
f80a0cbd07 | ||
|
|
9da753b915 | ||
|
|
5a8c9f5533 | ||
|
|
8a6ec093d3 | ||
|
|
bd434ff285 | ||
|
|
0571ec723d | ||
|
|
7545c83547 | ||
|
|
d66fadecda | ||
|
|
4d238b8ac6 | ||
|
|
02ef69f0f7 | ||
|
|
5584928489 | ||
|
|
67f0600702 | ||
|
|
3d9b3099f2 | ||
|
|
c11d56279f | ||
|
|
37ed800d6a | ||
|
|
35d7e1b32e | ||
|
|
5a6a14d1f3 | ||
|
|
8d35e7d3bd | ||
|
|
b678d848db | ||
|
|
d86b2c410f | ||
|
|
45bf7a234f | ||
|
|
b027eeb846 | ||
|
|
424df8c1ae | ||
|
|
d095b355cc | ||
|
|
70126748fd | ||
|
|
835e030c65 | ||
|
|
dbcd7442fe | ||
|
|
746361d495 | ||
|
|
8fd43e8cff | ||
|
|
8198872972 | ||
|
|
aae579c650 | ||
|
|
43c2bdf777 | ||
|
|
b7172da209 | ||
|
|
575f8335d6 | ||
|
|
a27be71b13 | ||
|
|
677bb041e9 | ||
|
|
190ae5a0bc | ||
|
|
bec12cdd55 | ||
|
|
1c8406ce3c | ||
|
|
eb69ddfef9 | ||
|
|
06db7c0641 | ||
|
|
542cfdf47d | ||
|
|
07b8e8fcf7 | ||
|
|
f3640446d2 | ||
|
|
0e74687910 | ||
|
|
417aeff21d | ||
|
|
46bc7d206d | ||
|
|
3c4cb60cda | ||
|
|
f05a61e8a8 | ||
|
|
181bc0c189 | ||
|
|
452c4ac89e | ||
|
|
8164e9712b | ||
|
|
a2c9fdb715 | ||
|
|
5c9b8addf3 | ||
|
|
e6f6c17c80 | ||
|
|
b8f9f23684 | ||
|
|
8f03f709fb | ||
|
|
6616f835af | ||
|
|
a7c29e4016 | ||
|
|
5adbd9519b | ||
|
|
d785f169c6 | ||
|
|
81252fd8bd | ||
|
|
02b80c2c6a | ||
|
|
7080ce72f6 | ||
|
|
133fdf03db | ||
|
|
8511821160 | ||
|
|
0c78f29907 | ||
|
|
cd10b56aa6 | ||
|
|
0531d0699a | ||
|
|
07dce5d474 | ||
|
|
bce85b6e45 | ||
|
|
f551b2c810 | ||
|
|
d2a1d7df44 | ||
|
|
df090d7970 | ||
|
|
dc278cc0e5 | ||
|
|
cdea0ff01b | ||
|
|
1a02837ada | ||
|
|
a59aba453f | ||
|
|
29f3ac9645 | ||
|
|
7c677f0fa1 | ||
|
|
362ab73026 | ||
|
|
fcdaf64268 | ||
|
|
fd4dcad80c | ||
|
|
3e3fd327d1 | ||
|
|
2e194361b3 | ||
|
|
92a852d28f | ||
|
|
f548f410b9 | ||
|
|
07f5b57f4c | ||
|
|
2c792975e0 | ||
|
|
0c771ca506 | ||
|
|
1ab691108e | ||
|
|
f0962e112a | ||
|
|
819e8bfecf | ||
|
|
d403bebfa0 | ||
|
|
a3ed07b3dc | ||
|
|
99e57ba888 |
133
.all-contributorsrc
Normal file
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"contributors": [
|
||||
{
|
||||
"login": "liyasthomas",
|
||||
"name": "Liyas Thomas",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/10395817?v=4",
|
||||
"profile": "https://liyasthomas.web.app",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NBTX",
|
||||
"name": "John Harker",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/43181178?v=4",
|
||||
"profile": "https://github.com/NBTX",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "larouxn",
|
||||
"name": "Nicholas La Roux",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1557529?v=4",
|
||||
"profile": "https://nicholaslaroux.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yubathom",
|
||||
"name": "Thomas Yuba",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/4117768?v=4",
|
||||
"profile": "https://github.com/yubathom",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nickpalenchar",
|
||||
"name": "Nick Palenchar",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7539781?v=4",
|
||||
"profile": "http://www.linkedin.com/in/nickpalenchar",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "terranblake",
|
||||
"name": "Terran Blake",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8795767?v=4",
|
||||
"profile": "https://www.lumahealth.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AndrewBastin",
|
||||
"name": "Andrew Bastin",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/9131943?v=4",
|
||||
"profile": "https://github.com/AndrewBastin",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vlad0337187",
|
||||
"name": "Vladislav",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/12682937?v=4",
|
||||
"profile": "https://github.com/vlad0337187",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "izerozlu",
|
||||
"name": "izerozlu",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/17386157?v=4",
|
||||
"profile": "https://github.com/izerozlu",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JacobAnavisca",
|
||||
"name": "Jacob Anavisca",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/21232366?v=4",
|
||||
"profile": "https://github.com/JacobAnavisca",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nityanandagohain",
|
||||
"name": "Nityananda Gohain",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/26831659?v=4",
|
||||
"profile": "http://nityanandagohain.github.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hosseinnedaee",
|
||||
"name": "Hossein Nedaee",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/42691357?v=4",
|
||||
"profile": "https://github.com/hosseinnedaee",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jamesgeorge007",
|
||||
"name": "James George",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/25279263?v=4",
|
||||
"profile": "https://ghuser.io/jamesgeorge007",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"projectName": "postwoman",
|
||||
"projectOwner": "liyasthomas",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com"
|
||||
}
|
||||
9
.dependabot/config.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: 1
|
||||
update_configs:
|
||||
- package_manager: "javascript"
|
||||
directory: "/"
|
||||
update_schedule: "weekly"
|
||||
default_labels:
|
||||
- "Dependency upgrade"
|
||||
default_reviewers:
|
||||
- "liyasthomas"
|
||||
104
.dockerignore
Normal file
@@ -0,0 +1,104 @@
|
||||
Dockerfile
|
||||
.vscode
|
||||
.github
|
||||
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# Mac OSX
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
14
.firebaserc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "postwoman-api"
|
||||
},
|
||||
"targets": {
|
||||
"postwoman-api": {
|
||||
"hosting": {
|
||||
"postwoman": [
|
||||
"postwoman"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
ko_fi: liyasthomas
|
||||
open_collective: liyasthomas
|
||||
patreon: liyasthomas
|
||||
custom: https://www.paypal.me/liyascthomas
|
||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**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):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
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.
|
||||
100
.gitignore
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# Mac OSX
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
53
.travis.yml
@@ -1,8 +1,53 @@
|
||||
# == INSTRUCTIONS FOR SETTING UP TRAVIS (and GitHub Pages) ==
|
||||
#
|
||||
# 1. Find this repository in your Travis-CI dashboard.
|
||||
# open settings and add an environment variable called
|
||||
# GITHUB_ACCESS_TOKEN and set it to your personal access token.addons:
|
||||
# See: https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line
|
||||
#
|
||||
# **DO NOT TURN ON 'Display value in build log'!!!!**
|
||||
#
|
||||
# 2. Push the code to the repository.
|
||||
# 3. Set the GitHub Pages source in the GitHub repository settings to the
|
||||
# gh-pages branch.
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "node"
|
||||
- "12"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgconf-2-4 # cypress binary dependency
|
||||
|
||||
env:
|
||||
- DEPLOY_ENV=POSTWOMAN_IO
|
||||
|
||||
cache:
|
||||
npm: true
|
||||
directories:
|
||||
- "node_modules"
|
||||
- ~/.cache
|
||||
|
||||
branches:
|
||||
only:
|
||||
- "master"
|
||||
|
||||
install:
|
||||
- "npm install firebase-tools"
|
||||
- "npm install"
|
||||
|
||||
before_script:
|
||||
- "npm run test"
|
||||
|
||||
script:
|
||||
- "cd functions"
|
||||
- "npm install"
|
||||
- "cd .."
|
||||
- "npm run generate"
|
||||
|
||||
notifications:
|
||||
webhooks: https://www.travisbuddy.com
|
||||
env:
|
||||
- MY_VAR=EverythignIsAwesome
|
||||
- NODE_ENV=TEST
|
||||
|
||||
after_success:
|
||||
- firebase deploy --token $FIREBASE_TOKEN
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deepcode.review.results.hideInformationIssues": false
|
||||
}
|
||||
80
404.html
@@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<script src="head.js"></script>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
|
||||
<title>mnmlurl - Minimal URL is a modern URL shortener with support for custom alias</title>
|
||||
<meta name="description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<meta itemprop="name" content="mnmlurl">
|
||||
<meta itemprop="description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta itemprop="image" content="icons/icon-192x192.png">
|
||||
<!-- See https://goo.gl/OOhYW5 -->
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<!-- See https://goo.gl/qRE0vM -->
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<!-- Add to homescreen for Chrome on Android. Fallback for manifest.json -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="application-name" content="mnmlurl">
|
||||
<!-- Add to homescreen for Safari on iOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="mnmlurl">
|
||||
<!-- Homescreen icons -->
|
||||
<link rel="apple-touch-icon" href="icons/icon-48x48.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="icons/icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="96x96" href="icons/icon-96x96.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="icons/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icons/icon-192x192.png">
|
||||
<!-- Tile icon for Windows 8 (144x144 + tile color) -->
|
||||
<meta name="msapplication-TileImage" content="icons/icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:site_name" content="mnmlurl">
|
||||
<meta property="og:url" content="https://mnmlurl.ml">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="mnmlurl">
|
||||
<meta property="og:description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta property="og:image" content="icons/icon-144x144.png">
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@liyasthomas">
|
||||
<meta name="twitter:creator" content="@liyasthomas">
|
||||
<meta name="twitter:url" content="https://mnmlurl.ml">
|
||||
<meta name="twitter:title" content="mnmlurl">
|
||||
<meta name="twitter:description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta name="twitter:image" content="icons/icon-144x144.png">
|
||||
<!-- Web Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="main">
|
||||
<div id="erbox">
|
||||
<h1>404</h1>
|
||||
page not found
|
||||
</div>
|
||||
<div class="flex"></div>
|
||||
<footer>
|
||||
<a href="https://mnmlurl.ml">
|
||||
<img src="icons/logo.svg" alt="logo" style="height: 24px; margin-right: 8px;">Home
|
||||
</a>
|
||||
<a href="https://github.com/liyasthomas/mnmlurl" target="_blank" rel="noopener">
|
||||
<img src="icons/github.svg" alt="GitHub" style="margin-right: 8px;">GitHub
|
||||
</a>
|
||||
<a href="https://github.com/liyasthomas/mnmlurl-extension" target="_blank" rel="noopener">
|
||||
Get browser extension
|
||||
</a>
|
||||
<button id="installPWA" onclick="installPWA()" style="padding: 16px;">
|
||||
<img src="icons/pwalogo.svg" alt="PWA" style="height: 16px;">
|
||||
</button>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
37
CHANGELOG.md
@@ -1,38 +1,3 @@
|
||||
# Changelog
|
||||
# Title
|
||||
### Description by [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
---
|
||||
|
||||
# 1.0.0
|
||||
|
||||
## This is it, Title 1.0.0!
|
||||
We are finally out of beta, therefore many bugs were fixed and camera received a brand new look.
|
||||
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: macOS and iOS support
|
||||
* **IMPROVEMENT**: Major improvements
|
||||
* **IMPROVEMENT**: Updated libraries
|
||||
* **FIX**: Fixed many bugs and crashes
|
||||
* **FIX**: Graphic glitches
|
||||
* **FIX**: Statusbar too dark
|
||||
* **TRANSLATION**: Updated translations
|
||||
* **REVERT**: Brought back the "Help" button
|
||||
* **OTHER**: Removed all analytics
|
||||
|
||||
---
|
||||
|
||||
# 0.9.0
|
||||
|
||||
## I worked a lot on Web apps, WebAR, WebGL & PWAs
|
||||
So I think Lvr is now ready to be released :)
|
||||
|
||||
I will keep the usual branch model.
|
||||
|
||||
* Stable release on `master` branch
|
||||
|
||||
---
|
||||
|
||||
## Thanks
|
||||
* [Google](https://www.google.com) - for [Polymer](https://polymer-project.org)
|
||||
* [v0.1.0](https://github.com/liyasthomas/postwoman/releases/tag/v0.1.0) - Initial 🎉 Initial public release
|
||||
226
CONTRIBUTING.md
@@ -1,180 +1,92 @@
|
||||
# Introduction
|
||||
# Contributing
|
||||
|
||||
### Write something nice here!
|
||||
When contributing to this repository, please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners of this repository before making a change.
|
||||
|
||||
>First off, thank you for considering contributing to Active Admin. It's people like you that make Active Admin such a great tool.
|
||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
||||
[source: [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Read The Docs](http://read-the-docs.readthedocs.org/en/latest/contribute.html) [2] [Mustache.js](https://github.com/janl/mustache.js/#contributing)
|
||||
## Pull Request Process
|
||||
|
||||
### Tell them why they should read your guidelines.
|
||||
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
|
||||
build.
|
||||
2. Update the README.md with details of changes to the interface, this includes new environment
|
||||
variables, exposed ports, useful file locations and container parameters.
|
||||
3. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
|
||||
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer to merge it for you.
|
||||
|
||||
>Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
|
||||
## Code of Conduct
|
||||
|
||||
[source: [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md)]
|
||||
### Our Pledge
|
||||
|
||||
### Explain what kinds of contributions you are looking for.
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
Keep an open mind! Improving documentation, bug triaging, or writing tutorials are all examples of helpful contributions that mean less work for you.
|
||||
### Our Standards
|
||||
|
||||
> Elasticsearch is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Elasticsearch itself.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Devise](https://github.com/plataformatec/devise/wiki/Contributing) [2] [Geocoder](https://github.com/alexreisner/geocoder#known-issue) (“known issue”)
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
### Explain contributions you are NOT looking for (if any).
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
Again, defining this up front means less work for you. If someone ignores your guide and submits something you don’t want, you can simply close it and point to your policy.
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
> Please, don't use the issue tracker for [support questions]. Check whether the #pocoo IRC channel on Freenode can help with your issue. If your problem is not strictly Werkzeug or Flask specific, #python is generally more active. Stack Overflow is also worth considering.
|
||||
### Our Responsibilities
|
||||
|
||||
[source: [Flask](https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#about-to-create-a-new-github-issue) [2] [Read the Docs](http://read-the-docs.readthedocs.org/en/latest/open-source-philosophy.html#unsupported)
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
# Ground Rules
|
||||
### Set expectations for behavior (yours, and theirs).
|
||||
This includes not just how to communicate with others (being respectful, considerate, etc) but also technical responsibilities (importance of testing, project dependencies, etc). Mention and link to your code of conduct, if you have one.
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
> Responsibilities
|
||||
> * Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux.
|
||||
> * Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2
|
||||
> * Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback.
|
||||
> * Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions.
|
||||
> * Keep feature versions as small as possible, preferably one new feature per version.
|
||||
> * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/).
|
||||
### Scope
|
||||
|
||||
[source: [cookiecutter](https://github.com/audreyr/cookiecutter/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#community-code-of-conduct) [2] [geocoder](https://github.com/alexreisner/geocoder#contributing)
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
# Your First Contribution
|
||||
Help people who are new to your project understand where they can be most helpful. This is also a good time to let people know if you follow a label convention for flagging beginner issues.
|
||||
### Enforcement
|
||||
|
||||
> Unsure where to begin contributing to Atom? You can start by looking through these beginner and help-wanted issues:
|
||||
> Beginner issues - issues which should only require a few lines of code, and a test or two.
|
||||
> Help wanted issues - issues which should be a bit more involved than beginner issues.
|
||||
> Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
[source: [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#your-first-code-contribution)] **Need more inspiration?** [1] [Read the Docs](http://docs.readthedocs.org/en/latest/contribute.html#contributing-to-development) [2] [Django](https://docs.djangoproject.com/en/dev/internals/contributing/new-contributors/#first-steps) (scroll down to "Guidelines" as well)
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Bonus points: Add a link to a resource for people who have never contributed to open source before.
|
||||
Here are a couple of friendly tutorials you can include: http://makeapullrequest.com/ and http://www.firsttimersonly.com/
|
||||
### Attribution
|
||||
|
||||
> Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[source: [React](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#pull-requests)]
|
||||
|
||||
As a side note, it helps to use newcomer-friendly language throughout the rest of your document. Here are a couple of examples from [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md):
|
||||
|
||||
>At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat:
|
||||
>
|
||||
>If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge.
|
||||
|
||||
# Getting started
|
||||
### Give them a quick walkthrough of how to submit a contribution.
|
||||
How you write this is up to you, but some things you may want to include:
|
||||
|
||||
* Let them know if they need to sign a CLA, agree to a DCO, or get any other legal stuff out of the way
|
||||
* If tests are required for contributions, let them know, and explain how to run the tests
|
||||
* If you use anything other than GitHub to manage issues (ex. JIRA or Trac), let them know which tools they’ll need to contribute
|
||||
|
||||
>For something that is bigger than a one or two line fix:
|
||||
|
||||
>1. Create your own fork of the code
|
||||
>2. Do the changes in your fork
|
||||
>3. If you like the change and think the project could use it:
|
||||
* Be sure you have followed the code style for the project.
|
||||
* Sign the Contributor License Agreement, CLA, with the jQuery Foundation.
|
||||
* Note the jQuery Foundation Code of Conduct.
|
||||
* Send a pull request indicating that you have a CLA on file.
|
||||
|
||||
[source: [Requirejs](http://requirejs.org/docs/contributing.html)] **Need more inspiration?** [1] [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md#1-where-do-i-go-from-here) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-contributions) [3] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#pull-requests)
|
||||
|
||||
### If you have a different process for small or "obvious" fixes, let them know.
|
||||
|
||||
> Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA.
|
||||
>
|
||||
>As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following:
|
||||
>* Spelling / grammar fixes
|
||||
>* Typo correction, white space and formatting changes
|
||||
>* Comment clean up
|
||||
>* Bug fixes that change default return values or error codes stored in constants
|
||||
>* Adding logging messages or debugging output
|
||||
>* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
|
||||
>* Moving source files from one directory or package to another
|
||||
|
||||
[source: [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#chef-obvious-fix-policy)] **Need more inspiration?** [1] [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#making-trivial-changes)
|
||||
|
||||
# How to report a bug
|
||||
### Explain security disclosures first!
|
||||
At bare minimum, include this sentence:
|
||||
> If you find a security vulnerability, do NOT open an issue. Email XXXX instead.
|
||||
|
||||
If you don’t want to use your personal contact information, set up a “security@” email address. Larger projects might have more formal processes for disclosing security, including encrypted communication. (Disclosure: I am not a security expert.)
|
||||
|
||||
> Any security issues should be submitted directly to security@travis-ci.org
|
||||
> In order to determine whether you are dealing with a security issue, ask yourself these two questions:
|
||||
> * Can I access something that's not mine, or something I shouldn't have access to?
|
||||
> * Can I disable something for other people?
|
||||
>
|
||||
> If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, just email us at security@travis-ci.org.
|
||||
|
||||
[source: [Travis CI](https://github.com/travis-ci/travis-ci/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#security) [2] [Express.js](https://github.com/expressjs/express/blob/master/Security.md)
|
||||
|
||||
### Tell your contributors how to file a bug report.
|
||||
You can even include a template so people can just copy-paste (again, less work for you).
|
||||
|
||||
> When filing an issue, make sure to answer these five questions:
|
||||
>
|
||||
> 1. What version of Go are you using (go version)?
|
||||
> 2. What operating system and processor architecture are you using?
|
||||
> 3. What did you do?
|
||||
> 4. What did you expect to see?
|
||||
> 5. What did you see instead?
|
||||
> General questions should go to the golang-nuts mailing list instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
[source: [Go](https://github.com/golang/go/blob/master/CONTRIBUTING.md#filing-issues)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#other-bugs ) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#reporting-bugs) (includes template)
|
||||
|
||||
# How to suggest a feature or enhancement
|
||||
### If you have a particular roadmap, goals, or philosophy for development, share it here.
|
||||
This information will give contributors context before they make suggestions that may not align with the project’s needs.
|
||||
|
||||
> The Express philosophy is to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs.
|
||||
>
|
||||
> Express does not force you to use any specific ORM or template engine. With support for over 14 template engines via Consolidate.js, you can quickly craft your perfect framework.
|
||||
|
||||
[source: [Express](https://github.com/expressjs/express#philosophy)] **Need more inspiration?** [Active Admin](https://github.com/activeadmin/activeadmin#goals)
|
||||
|
||||
### Explain your desired process for suggesting a feature.
|
||||
If there is back-and-forth or signoff required, say so. Ask them to scope the feature, thinking through why it’s needed and how it might work.
|
||||
|
||||
> If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need. Open an issue on our issues list on GitHub which describes the feature you would like to see, why you need it, and how it should work.
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#feature-requests)] **Need more inspiration?** [1] [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md#feature-requests) [2] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#requesting-a-feature)
|
||||
|
||||
# Code review process
|
||||
### Explain how a contribution gets accepted after it’s been submitted.
|
||||
Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all?
|
||||
|
||||
> The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public Google Hangout. The hangout is announced in the weekly status updates that are sent to the puppet-dev list. Notes are posted to the Puppet Community community-triage repo and include a link to a YouTube recording of the hangout.
|
||||
> After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity.
|
||||
|
||||
[source: [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#submitting-changes)] **Need more inspiration?** [1] [Meteor](https://meteor.hackpad.com/Responding-to-GitHub-Issues-SKE2u3tkSiH ) [2] [Express.js](https://github.com/expressjs/express/blob/master/Contributing.md#becoming-a-committer)
|
||||
|
||||
# Community
|
||||
If there are other channels you use besides GitHub to discuss contributions, mention them here. You can also list the author, maintainers, and/or contributors here, or set expectations for response time.
|
||||
|
||||
> You can chat with the core team on https://gitter.im/cucumber/cucumber. We try to have office hours on Fridays.
|
||||
|
||||
[source: [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#talking-with-other-devs)] **Need more inspiration?**
|
||||
[1] [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-developer-office-hours) [2] [Cookiecutter](https://github.com/audreyr/cookiecutter#community)
|
||||
|
||||
# BONUS: Code, commit message and labeling conventions
|
||||
These sections are not necessary, but can help streamline the contributions you receive.
|
||||
|
||||
### Explain your preferred style for code, if you have any.
|
||||
|
||||
**Need inspiration?** [1] [Requirejs](http://requirejs.org/docs/contributing.html#codestyle) [2] [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#contributing-to-the-elasticsearch-codebase)
|
||||
|
||||
### Explain if you use any commit message conventions.
|
||||
|
||||
**Need inspiration?** [1] [Angular](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#submit) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#step-3-commit)
|
||||
|
||||
### Explain if you use any labeling conventions for issues.
|
||||
|
||||
**Need inspiration?** [1] [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels#standardissuelabels) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#issue-and-pull-request-labels)
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM node:12.10.0-buster
|
||||
|
||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
328
README.md
@@ -1,46 +1,173 @@
|
||||
```
|
||||
When I wrote this, only God and I understood what I was doing. Now, only God knows.
|
||||
```
|
||||
<div align="center">
|
||||
<a href="https://liyas-thomas.firebaseapp.com"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Liyas Thomas" width="200"></a>
|
||||
<a href="https://postwoman.io"><img src="static/logo.png" alt="Postwoman.io logo" height="160"></a>
|
||||
<br>
|
||||
<h1>Liyas Thomas</h1>
|
||||
<sub>Built with ❤︎ by
|
||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
||||
</sub>
|
||||
<br>
|
||||
<p>
|
||||
API request builder - A free, fast, and beautiful alternative to Postman
|
||||
</p>
|
||||
<p>
|
||||
Helps you create your requests faster, saving you precious time on your development - <a href="https://postwoman.launchaco.com">Subscribe</a>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
[](https://travis-ci.com/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://postwoman.io) [](CONTRIBUTING.md) [](#contributors) [](https://opencollective.com/postwoman) [](https://www.paypal.me/liyascthomas) [](https://t.me/postwoman_app) [](https://discord.gg/GAMWxmR) [](https://twitter.com/intent/tweet?url=https%3A%2F%2Fpostwoman.io&text=%F0%9F%91%BD%20Postwoman%20%E2%80%A2%20API%20request%20builder%20-%20Helps%20you%20create%20your%20requests%20faster%2C%20saving%20you%20precious%20time%20on%20your%20development&original_referer=https%3A%2F%2Ftwitter.com%2Fshare%3Ftext%3D%25F0%259F%2591%25BD%2520Postwoman%2520%25E2%2580%25A2%2520API%2520request%2520builder%2520-%2520Helps%2520you%2520create%2520your%2520requests%2520faster%2C%2520saving%2520you%2520precious%2520time%2520on%2520your%2520development%26url%3Dhttps%3A%2F%2Fpostwoman.io%26hashtags%3Dpostwoman%26via%3Dliyasthomas&via=liyasthomas&hashtags=postwoman)
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<sub>Built with ❤︎ by
|
||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
||||
</sub>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[](https://travis-ci.org/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://github.com/liyasthomas/postwoman/archive/master.zip) [](https://github.com/liyasthomas/postwoman/blob/master/LICENSE) [](https://github.com/liyasthomas/postwoman/issues) [](https://liyas-thomas.firebaseapp.com) [](https://www.paypal.me/liyascthomas)
|
||||
**Start here: _[Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md)_**
|
||||
|
||||
# <img src="icons/icon-48x48.png" alt="postwoman" width="32"> Postwoman
|
||||
**Chat here: _[Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)_**
|
||||
|
||||
### 👽 API request builder by [Liyas Thomas](https://github.com/liyasthomas)
|
||||
**Donate here: _[PayPal](https://www.paypal.me/liyascthomas), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas)_**
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<img src="icons/screely.png" alt="postwoman" width="100%">
|
||||
<img src="icons/screely2.png" alt="postwoman" width="100%">
|
||||
<img src="static/images/screenshot1.png" alt="Screenshot1" width="100%">
|
||||
<img src="static/images/screenshot2.png" alt="Screenshot2" width="100%">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
### Features :sparkles:
|
||||
### Features ✨
|
||||
|
||||
:heart: **Lightweight and minimal**: Crafted with minimalistic UI design
|
||||
❤️ **Lightweight**: Crafted with minimalistic UI design. Simple design is the best design.
|
||||
|
||||
:electric_plug: **Real-time demo**: Send requests and get response right away!
|
||||
- Faster, lighter, cleaner, minimal & responsive
|
||||
|
||||
:robot: **VIBGYOR**: Neon combination of colors for background and foreground
|
||||
⚡️ **Fast**: Send requests and get/copy responses in real-time! Fast software is the best software.
|
||||
|
||||
:sparkles: **PWA**: Install as a PWA on your device
|
||||
**Methods:**
|
||||
- `GET` - Retrieve information about the REST API resource
|
||||
- `HEAD` - Retrieve response headers identical to those of a GET request, but without the response body.
|
||||
- `POST` - Create a REST API resource
|
||||
- `PUT` - Update a REST API resource
|
||||
- `DELETE` - Delete a REST API resource or related component
|
||||
- `OPTIONS` - Describe the communication options for the target resource
|
||||
- `PATCH` - Apply partial modifications to a REST API resource
|
||||
|
||||
---
|
||||
_History entries are synced with local session storage_
|
||||
|
||||
## Demo
|
||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors: because customization === freedom. [Customize now ✨](https://postwoman.io/settings).
|
||||
|
||||
[https://liyasthomas.github.io/postwoman](https://liyasthomas.github.io/postwoman)
|
||||
**Customizations:**
|
||||
- Choose theme: Kinda Dark (default), Clearly White, Just Black and System theme
|
||||
- Choose accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
||||
- Toggle multi-colored frames
|
||||
|
||||
_Customized themes are also synced with local session storage_
|
||||
|
||||
🔥 **PWA**: Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||
|
||||
**Features:**
|
||||
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
||||
- Offline support
|
||||
- Low RAM/memory and CPU usage
|
||||
- [Add to Home Screen](https://developers.google.com/web/fundamentals/app-install-banners) (button in footer)
|
||||
- [Desktop PWA](https://developers.google.com/web/progressive-web-apps/desktop) support (button in footer)
|
||||
- ([full features](https://developers.google.com/web/progressive-web-apps))
|
||||
|
||||
🚀 **Request**: Retrieve data from a URL without having to do a full page refresh.
|
||||
|
||||
- Choose `method`
|
||||
- Enter `URL`
|
||||
- Enter `Path`
|
||||
|
||||
**Features:**
|
||||
- Copy/share public "Share URL"
|
||||
- Generate request code for JavaScript XHR, Fetch and cURL
|
||||
- Copy generated request code to clipboard
|
||||
- Import cURL
|
||||
- Label requests
|
||||
|
||||
🔌 **Web Socket**: Establish full-duplex communication channels over a single TCP connection.
|
||||
|
||||
- Send and receive data
|
||||
|
||||
🔐 **Authentication**: Allows to identity the end user.
|
||||
|
||||
**Types:**
|
||||
- None
|
||||
- Basic authentication using username and password
|
||||
- Token based authentication
|
||||
|
||||
📢 **Headers**: Describes the format the body of your request is being sent as.
|
||||
|
||||
- Add or remove Header list
|
||||
|
||||
📫 **Parameters**: Use request parameters to set varying parts in simulated requests.
|
||||
|
||||
📃 **Request Body**: Used to send and receive data via the REST API.
|
||||
|
||||
**Options:**
|
||||
- Set Content Type
|
||||
- Add or remove Parameter list
|
||||
- Toggle between key-value and RAW input Parameter list
|
||||
|
||||
👋 **Responses**: Contains the status line, headers and the message/response body.
|
||||
|
||||
- Copy response to clipboard
|
||||
- View preview for HTML responses
|
||||
|
||||
_HTML responses have "Preview HTML" feature_
|
||||
|
||||
⏰ **History**: Request entries are synced with local session storage to reuse with a single click.
|
||||
|
||||
**Fields:**
|
||||
- Label
|
||||
- Timestamp
|
||||
- Method
|
||||
- Status code
|
||||
- URL
|
||||
- Path
|
||||
|
||||
_History entries can be sorted by any fields_
|
||||
|
||||
_Histories can deleted one-by-one or all together_
|
||||
|
||||
📁 **Collections**: Keep your API requests organized with collections and folders. Reuse them with a single click.
|
||||
|
||||
**Options:**
|
||||
- Create infinite collections, folders and requests
|
||||
- Edit, delete, move, export, import and replace
|
||||
|
||||
_Export, import and replace collections with JSON files_
|
||||
|
||||
_Collections are synced with local session storage_
|
||||
|
||||
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
|
||||
|
||||
**Features:**
|
||||
- Hide your IP address
|
||||
- Fixes [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||
- Access APIs served in non-HTTPS (`http://`, `localhost`, etc.)
|
||||
|
||||
_Proxy is hosted by ApolloTV - [Privacy policy](https://apollotv.xyz/legal)_
|
||||
|
||||
📜 **Pre-Request Scripts β**: Snippets of code associated with a request that are executed before the request is sent.
|
||||
|
||||
**Use-cases:**
|
||||
- Including the timestamp in the request headers
|
||||
- Sending a random alphanumeric string in the URL parameters
|
||||
|
||||
_Pre-Request Scripts is an experimental feature and is in Public Beta testing_
|
||||
|
||||
_Requests with Pre-Request Scripts are indicated in History entries_
|
||||
|
||||
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
|
||||
|
||||
## Demo 🚀 [](https://postwoman.io)
|
||||
|
||||
[https://postwoman.io](https://postwoman.io)
|
||||
|
||||
## Usage 💡
|
||||
|
||||
1. Specify your request method
|
||||
2. Type in your API URL
|
||||
@@ -50,67 +177,160 @@ When I wrote this, only God and I understood what I was doing. Now, only God kno
|
||||
|
||||
You're done!
|
||||
|
||||
---
|
||||
## Built with 🔧
|
||||
|
||||
## Built with
|
||||
|
||||
* **[Chromium](https://github.com/chromium/chromium)** - Thanks for being so fast!
|
||||
* [Chromium](https://github.com/chromium/chromium) - Thanks for being so fast!
|
||||
* HTML - For the web framework
|
||||
* CSS - For styling components
|
||||
* JavaScript - For magic!
|
||||
* [Vue](https://vuejs.org/) - To add to the JavaScript magic!
|
||||
* [Nuxt](https://nuxtjs.org/) - To add to the Vue magic!
|
||||
|
||||
---
|
||||
## Developing 👷
|
||||
|
||||
## Contributing
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Start the development server with `npm run dev`.
|
||||
4. Open development site by going to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
#### Or, with docker-compose:
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Run `docker-compose up`
|
||||
3. Open development site by going to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
## Docker 🐳 [](https://hub.docker.com/r/liyasthomas/postwoman)
|
||||
|
||||
```bash
|
||||
#pull
|
||||
docker pull liyasthomas/postwoman
|
||||
|
||||
#run
|
||||
docker run -p 3000:3000 liyasthomas/postwoman:latest
|
||||
|
||||
#build
|
||||
docker build -t postwoman:latest
|
||||
```
|
||||
|
||||
## Releasing 🏷️
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Build the release files with `npm run build`.
|
||||
4. Find the built project in `./dist`.
|
||||
|
||||
## Contributing 🍰
|
||||
|
||||
Please read [CONTRIBUTING](CONTRIBUTING.md) for details on our [CODE OF CONDUCT](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||
|
||||
---
|
||||
## Continuous Integration 💚 [](https://travis-ci.com/liyasthomas/postwoman)
|
||||
|
||||
## Continuous Integration
|
||||
We use [Travis CI](https://travis-ci.com) for continuous integration. Check out our [Travis CI Status](https://travis-ci.com/liyasthomas/postwoman).
|
||||
|
||||
We use [Travis CI](https://travis-ci.com) for continuous integration. Check out our [Travis CI Status](https://travis-ci.org/liyasthomas/postwoman).
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
## Versioning 🔖 [](https://github.com/liyasthomas/postwoman/releases/latest)
|
||||
|
||||
This project is developed by [Liyas Thomas](https://github.com/liyasthomas) using the [Semantic Versioning specification](https://semver.org). For the versions available, see the [releases on this repository](https://github.com/liyasthomas/postwoman/releases).
|
||||
|
||||
---
|
||||
|
||||
## Change log
|
||||
## Change log 📝
|
||||
|
||||
See the [CHANGELOG](CHANGELOG.md) file for details.
|
||||
|
||||
---
|
||||
|
||||
## Authors
|
||||
## Authors 🔮
|
||||
|
||||
### Lead Developers
|
||||
* [**Liyas Thomas**](https://github.com/liyasthomas) - *Author*
|
||||
|
||||
* **[Liyas Thomas](https://github.com/liyasthomas)** - *Author*
|
||||
* **[Caneco](https://twitter.com/caneco)** - *Designer*
|
||||
|
||||
### Testing and Debugging
|
||||
* [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
### Contributors
|
||||
* [Liyas Thomas](https://github.com/liyasthomas)
|
||||
* ([contributors](https://github.com/liyasthomas/postwoman/graphs/contributors))
|
||||
|
||||
### Thanks
|
||||
* [Dribbble](https://dribbble.com)
|
||||
### Collaborators <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://liyasthomas.web.app"><img src="https://avatars1.githubusercontent.com/u/10395817?v=4" width="100px;" alt="Liyas Thomas"/><br /><sub><b>Liyas Thomas</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=liyasthomas" title="Code">💻</a> <a href="#design-liyasthomas" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/NBTX"><img src="https://avatars3.githubusercontent.com/u/43181178?v=4" width="100px;" alt="John Harker"/><br /><sub><b>John Harker</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=NBTX" title="Code">💻</a> <a href="#design-NBTX" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://nicholaslaroux.com"><img src="https://avatars0.githubusercontent.com/u/1557529?v=4" width="100px;" alt="Nicholas La Roux"/><br /><sub><b>Nicholas La Roux</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=larouxn" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/yubathom"><img src="https://avatars3.githubusercontent.com/u/4117768?v=4" width="100px;" alt="Thomas Yuba"/><br /><sub><b>Thomas Yuba</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=yubathom" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.linkedin.com/in/nickpalenchar"><img src="https://avatars1.githubusercontent.com/u/7539781?v=4" width="100px;" alt="Nick Palenchar"/><br /><sub><b>Nick Palenchar</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nickpalenchar" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.lumahealth.io/"><img src="https://avatars3.githubusercontent.com/u/8795767?v=4" width="100px;" alt="Terran Blake"/><br /><sub><b>Terran Blake</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=terranblake" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/AndrewBastin"><img src="https://avatars2.githubusercontent.com/u/9131943?v=4" width="100px;" alt="Andrew Bastin"/><br /><sub><b>Andrew Bastin</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=AndrewBastin" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/vlad0337187"><img src="https://avatars1.githubusercontent.com/u/12682937?v=4" width="100px;" alt="Vladislav"/><br /><sub><b>Vladislav</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=vlad0337187" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/izerozlu"><img src="https://avatars3.githubusercontent.com/u/17386157?v=4" width="100px;" alt="izerozlu"/><br /><sub><b>izerozlu</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=izerozlu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/JacobAnavisca"><img src="https://avatars2.githubusercontent.com/u/21232366?v=4" width="100px;" alt="Jacob Anavisca"/><br /><sub><b>Jacob Anavisca</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=JacobAnavisca" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://nityanandagohain.github.io"><img src="https://avatars3.githubusercontent.com/u/26831659?v=4" width="100px;" alt="Nityananda Gohain"/><br /><sub><b>Nityananda Gohain</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nityanandagohain" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/hosseinnedaee"><img src="https://avatars2.githubusercontent.com/u/42691357?v=4" width="100px;" alt="Hossein Nedaee"/><br /><sub><b>Hossein Nedaee</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=hosseinnedaee" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://ghuser.io/jamesgeorge007"><img src="https://avatars2.githubusercontent.com/u/25279263?v=4" width="100px;" alt="James George"/><br /><sub><b>James George</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=jamesgeorge007" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
See the list of [contributors](https://github.com/liyasthomas/postwoman/graphs/contributors) who participated in this project.
|
||||
|
||||
---
|
||||
### Thanks
|
||||
|
||||
## License
|
||||
* [dev.to 👩💻👨💻](https://dev.to)
|
||||
|
||||
### Code Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors"><img src="https://opencollective.com/postwoman/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/postwoman/contribute)]
|
||||
|
||||
#### Individuals
|
||||
|
||||
<a href="https://opencollective.com/postwoman"><img src="https://opencollective.com/postwoman/individuals.svg"></a>
|
||||
|
||||
#### Organizations
|
||||
|
||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/postwoman/contribute)]
|
||||
|
||||
<a href="https://opencollective.com/postwoman/organization/0/website"><img src="https://opencollective.com/postwoman/organization/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/1/website"><img src="https://opencollective.com/postwoman/organization/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/2/website"><img src="https://opencollective.com/postwoman/organization/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/3/website"><img src="https://opencollective.com/postwoman/organization/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/4/website"><img src="https://opencollective.com/postwoman/organization/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/5/website"><img src="https://opencollective.com/postwoman/organization/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/6/website"><img src="https://opencollective.com/postwoman/organization/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/7/website"><img src="https://opencollective.com/postwoman/organization/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/8/website"><img src="https://opencollective.com/postwoman/organization/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/9/website"><img src="https://opencollective.com/postwoman/organization/9/avatar.svg"></a>
|
||||
|
||||
## License 📄
|
||||
|
||||
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
---
|
||||
## Acknowledgements 🙏
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
* Hat tip to anyone who's code was used
|
||||
* Hat tip to anyone whose code was used
|
||||
* Inspirations:
|
||||
* [Dribbble](https://dribbble.com)
|
||||
* [Dribbble](https://dribbble.com)
|
||||
|
||||
## Badges
|
||||
|
||||
| Status | Preview | Markdown Code (copy & paste into `readme.md`) |
|
||||
| ----------- | ----------- | ----------- |
|
||||
| **Default** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Success** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Critical** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Custom** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| Make your own badge! | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Postwoman.io" width="200"></a>
|
||||
<br>
|
||||
<h3>Happy Coding ❤︎</h3>
|
||||
</div>
|
||||
|
||||
7
assets/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# ASSETS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
|
||||
63
assets/css/fonts.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Material Design Icons */
|
||||
@font-face {
|
||||
font-family: "Material Icons";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(~@/assets/fonts/material-icons-v48.woff2) format("woff2");
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: "Material Icons";
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: "liga";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Roboto Mono 400 */
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Mono"), local("RobotoMono-Regular"),
|
||||
url("~@/assets/fonts/roboto-mono-v7-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* Poppins 500 */
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Poppins Medium"), local("Poppins-Medium"),
|
||||
url("~@/assets/fonts/poppins-v9-latin-500.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/poppins-v9-latin-500.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* poppins-700 - latin */
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local("Poppins Bold"), local("Poppins-Bold"),
|
||||
url("~@/assets/fonts/poppins-v9-latin-700.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/poppins-v9-latin-700.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
765
assets/css/styles.scss
Normal file
@@ -0,0 +1,765 @@
|
||||
$responsiveWidth: 720px;
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--ac-sel-color);
|
||||
color: var(--act-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--fg-light-color);
|
||||
border-radius: 8px;
|
||||
border: 2px solid var(--bg-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--fg-color);
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: var(--fg-light-color);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-color);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
line-height: 1.5;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Make theme transition smoother.
|
||||
body.afterLoad {
|
||||
|
||||
&,
|
||||
& * {
|
||||
transition: background-color 0.2s ease-in-out, border 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
body.sticky-footer footer {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-flex;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
& > div {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wrapper .content {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header,
|
||||
.content,
|
||||
.columns,
|
||||
.footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.slide-in {
|
||||
position: relative;
|
||||
animation: slideIn 0.2s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
left: -16px;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 22px;
|
||||
color: var(--ac-color);
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
display: flex;
|
||||
order: 1;
|
||||
flex-flow: column;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
order: 2;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.nav-second {
|
||||
display: flex;
|
||||
width: 10%;
|
||||
order: 3;
|
||||
// comment this to display
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
border-bottom: 1px solid var(--brd-color);
|
||||
|
||||
svg {
|
||||
fill: var(--fg-light-color);
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--brd-color);
|
||||
color: var(--fg-light-color);
|
||||
margin: 8px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--fg-color);
|
||||
|
||||
svg {
|
||||
fill: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.nuxt-link-exact-active {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
border-radius: 16px;
|
||||
|
||||
svg {
|
||||
fill: var(--act-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav.secondary-nav {
|
||||
display: flex;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-light-color);
|
||||
margin: 8px;
|
||||
|
||||
&:hover {
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
&.current {
|
||||
color: var(--ac-color);
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
$responsiveWidth: 720px;
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
.columns {
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
width: 100%;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
nav.secondary-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: block !important;
|
||||
z-index: 10000;
|
||||
|
||||
.tooltip-inner {
|
||||
background: black;
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
margin: 5px;
|
||||
border-color: black;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&[x-placement^="top"] {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="bottom"] {
|
||||
margin-top: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
top: -5px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="right"] {
|
||||
margin-left: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 5px 5px 0;
|
||||
border-left-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
left: -5px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="left"] {
|
||||
margin-right: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-top-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
right: -5px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.popover {
|
||||
$color: #f9f9f9;
|
||||
|
||||
.popover-inner {
|
||||
background: $color;
|
||||
color: black;
|
||||
padding: 24px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 5px 30px rgba(black, 0.1);
|
||||
}
|
||||
|
||||
.popover-arrow {
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
&[aria-hidden="true"] {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, visibility 0.15s;
|
||||
}
|
||||
|
||||
&[aria-hidden="false"] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
}
|
||||
|
||||
h3.title {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 4px;
|
||||
padding: 0 16px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
transition: all 0.2s ease-in-out;
|
||||
fill: var(--act-color);
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
display: inline-flex;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&.icon {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: var(--act-color);
|
||||
fill: var(--act-color);
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 16px 0;
|
||||
border: 1px solid var(--brd-color);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
legend {
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--fg-color);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset.blue legend {
|
||||
color: #57b5f9;
|
||||
}
|
||||
|
||||
fieldset.gray legend {
|
||||
color: #bcc2cd;
|
||||
}
|
||||
|
||||
fieldset.green legend {
|
||||
color: #50fa7b;
|
||||
}
|
||||
|
||||
fieldset.cyan legend {
|
||||
color: #8be9fd;
|
||||
}
|
||||
|
||||
fieldset.purple legend {
|
||||
color: #bd93f9;
|
||||
}
|
||||
|
||||
fieldset.orange legend {
|
||||
color: #ffb86c;
|
||||
}
|
||||
|
||||
fieldset.pink legend {
|
||||
color: #ff79c6;
|
||||
}
|
||||
|
||||
fieldset.red legend {
|
||||
color: #ff5555;
|
||||
}
|
||||
|
||||
fieldset.yellow legend {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
select,
|
||||
input,
|
||||
option,
|
||||
textarea,
|
||||
pre {
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
font-size: 16px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
transition: all 0.2s ease-in-out;
|
||||
user-select: text;
|
||||
width: calc(100% - 8px);
|
||||
min-height: 40px;
|
||||
resize: vertical;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not([readonly]):hover {
|
||||
background-color: var(--bg-dark-color);
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
code {
|
||||
height: 336px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
background-color: var(--bg-dark-color) !important;
|
||||
color: var(--fg-color) !important;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
|
||||
select,
|
||||
input,
|
||||
option {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: none;
|
||||
|
||||
&,
|
||||
& + label {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
|
||||
&:before {
|
||||
content: "\2714";
|
||||
border: 1px solid var(--fg-color);
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 8px 8px 8px 0;
|
||||
color: transparent;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + label:before {
|
||||
background-color: var(--ac-color);
|
||||
border-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
|
||||
.error,
|
||||
.disabled,
|
||||
[disabled] {
|
||||
background-color: var(--err-color);
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
cursor: default;
|
||||
|
||||
&.icon {
|
||||
color: var(--bg-color);
|
||||
fill: var(--bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
display: flex;
|
||||
margin: 4px 0 4px;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hide-on-small-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
#installPWA {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.info-response {
|
||||
color: #ffeb3b;
|
||||
}
|
||||
|
||||
.success-response {
|
||||
color: #4bb543;
|
||||
}
|
||||
|
||||
.redir-response {
|
||||
color: #ff5722;
|
||||
}
|
||||
|
||||
.cl-error-response {
|
||||
color: #a63232;
|
||||
}
|
||||
|
||||
.sv-error-response {
|
||||
color: #b71c1c;
|
||||
}
|
||||
|
||||
.missing-data-response {
|
||||
background-color: var(--err-color);
|
||||
}
|
||||
|
||||
.virtual-list::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
fieldset#history {
|
||||
.method-list-item {
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#response-details-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
|
||||
textarea {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.covers-response {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#send {
|
||||
#hidden-message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.show {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
z-index: 1;
|
||||
|
||||
#hidden-message {
|
||||
display: block;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.tab {
|
||||
width: 100%;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
div.tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="radio"] + label {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--brd-color);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
border-color: var(--fg-color);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label + div.tab {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toasted-container .toasted {
|
||||
justify-content: start !important;
|
||||
}
|
||||
|
||||
.toasted.info {
|
||||
background-color: var(--ac-color) !important;
|
||||
color: var(--act-color) !important;
|
||||
}
|
||||
|
||||
.toasted.bubble .action {
|
||||
color: inherit !important;
|
||||
}
|
||||
98
assets/css/themes.scss
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
Main Themes:
|
||||
- dark (default)
|
||||
- light
|
||||
- black
|
||||
- auto
|
||||
*/
|
||||
|
||||
// Dark is the default theme variant.
|
||||
@mixin darkTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(41, 42, 45);
|
||||
// Background color
|
||||
--bg-color: rgb(37, 38, 40);
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(49, 49, 55);
|
||||
// Text color
|
||||
--fg-color: rgb(247, 248, 248);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: rgb(48, 47, 55);
|
||||
// Error color
|
||||
--err-color: rgb(41, 42, 45);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
--ac-sel-color: rgb(80, 250, 123, 0.8);
|
||||
// Active text color
|
||||
--act-color: rgb(37, 38, 40);
|
||||
}
|
||||
|
||||
:root {
|
||||
@include darkTheme;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
:root.auto {
|
||||
@include darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lightTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: #f6f6f6;
|
||||
// Background color
|
||||
--bg-color: #ffffff;
|
||||
// Auto-complete color
|
||||
--atc-color: #ebebeb;
|
||||
// Text color
|
||||
--fg-color: #525252;
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: #eeeeed;
|
||||
// Error color
|
||||
--err-color: #f6f6f6;
|
||||
// Acent color
|
||||
--ac-color: #57b5f9;
|
||||
--ac-sel-color: #57b5f9;
|
||||
// Active text color
|
||||
--act-color: #ffffff;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@include lightTheme;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: light) {
|
||||
:root.auto {
|
||||
@include lightTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin blackTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(8, 8, 8);
|
||||
// Background color
|
||||
--bg-color: #000000;
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(18, 18, 18);
|
||||
// Text color
|
||||
--fg-color: rgb(250, 250, 250);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(100, 100, 100);
|
||||
// Border color
|
||||
--brd-color: rgb(16, 16, 16);
|
||||
// Error color
|
||||
--err-color: rgb(8, 8, 8);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
--ac-sel-color: rgb(80, 250, 123, 0.8);
|
||||
// Active text color
|
||||
--act-color: #000000;
|
||||
}
|
||||
|
||||
:root.black {
|
||||
@include blackTheme;
|
||||
}
|
||||
BIN
assets/fonts/material-icons-v48.woff2
Normal file
BIN
assets/fonts/poppins-v9-latin-500.woff
Normal file
BIN
assets/fonts/poppins-v9-latin-500.woff2
Normal file
BIN
assets/fonts/poppins-v9-latin-700.woff
Normal file
BIN
assets/fonts/poppins-v9-latin-700.woff2
Normal file
BIN
assets/fonts/roboto-mono-v7-latin-regular.woff
Normal file
BIN
assets/fonts/roboto-mono-v7-latin-regular.woff2
Normal file
228
assets/js/curlparser.js
Normal file
@@ -0,0 +1,228 @@
|
||||
import * as cookie from "cookie";
|
||||
import * as URL from "url";
|
||||
import * as querystring from "querystring";
|
||||
|
||||
/**
|
||||
* given this: [ 'msg1=value1', 'msg2=value2' ]
|
||||
* output this: 'msg1=value1&msg2=value2'
|
||||
* @param dataArguments
|
||||
*/
|
||||
function joinDataArguments(dataArguments) {
|
||||
let data = "";
|
||||
dataArguments.forEach(function (argument, i) {
|
||||
if (i === 0) {
|
||||
data += argument;
|
||||
} else {
|
||||
data += "&" + argument;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
function parseCurlCommand(curlCommand) {
|
||||
let newlineFound = /\r|\n/.exec(curlCommand);
|
||||
if (newlineFound) {
|
||||
// remove newlines
|
||||
curlCommand = curlCommand.replace(/\\\r|\\\n/g, "");
|
||||
}
|
||||
// yargs parses -XPOST as separate arguments. just prescreen for it.
|
||||
curlCommand = curlCommand.replace(/ -XPOST/, " -X POST");
|
||||
curlCommand = curlCommand.replace(/ -XGET/, " -X GET");
|
||||
curlCommand = curlCommand.replace(/ -XPUT/, " -X PUT");
|
||||
curlCommand = curlCommand.replace(/ -XPATCH/, " -X PATCH");
|
||||
curlCommand = curlCommand.replace(/ -XDELETE/, " -X DELETE");
|
||||
curlCommand = curlCommand.trim();
|
||||
let parsedArguments = require("yargs-parser")(curlCommand);
|
||||
let cookieString;
|
||||
let cookies;
|
||||
let url = parsedArguments._[1];
|
||||
if (!url) {
|
||||
for (let argName in parsedArguments) {
|
||||
if (typeof parsedArguments[argName] === "string") {
|
||||
if (["http", "www."].includes(parsedArguments[argName])) {
|
||||
url = parsedArguments[argName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let headers;
|
||||
|
||||
let parseHeaders = function (headerFieldName) {
|
||||
if (parsedArguments[headerFieldName]) {
|
||||
if (!headers) {
|
||||
headers = {};
|
||||
}
|
||||
if (!Array.isArray(parsedArguments[headerFieldName])) {
|
||||
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]];
|
||||
}
|
||||
parsedArguments[headerFieldName].forEach(function (header) {
|
||||
if (header.includes("Cookie")) {
|
||||
// stupid javascript tricks: closure
|
||||
cookieString = header;
|
||||
} else {
|
||||
let colonIndex = header.indexOf(":");
|
||||
let headerName = header.substring(0, colonIndex);
|
||||
let headerValue = header.substring(colonIndex + 1).trim();
|
||||
headers[headerName] = headerValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
parseHeaders("H");
|
||||
parseHeaders("header");
|
||||
if (parsedArguments.A) {
|
||||
if (!headers) {
|
||||
headers = [];
|
||||
}
|
||||
headers["User-Agent"] = parsedArguments.A;
|
||||
} else if (parsedArguments["user-agent"]) {
|
||||
if (!headers) {
|
||||
headers = [];
|
||||
}
|
||||
headers["User-Agent"] = parsedArguments["user-agent"];
|
||||
}
|
||||
|
||||
if (parsedArguments.b) {
|
||||
cookieString = parsedArguments.b;
|
||||
}
|
||||
if (parsedArguments.cookie) {
|
||||
cookieString = parsedArguments.cookie;
|
||||
}
|
||||
let multipartUploads;
|
||||
if (parsedArguments.F) {
|
||||
multipartUploads = {};
|
||||
if (!Array.isArray(parsedArguments.F)) {
|
||||
parsedArguments.F = [parsedArguments.F];
|
||||
}
|
||||
parsedArguments.F.forEach(function (multipartArgument) {
|
||||
// input looks like key=value. value could be json or a file path prepended with an @
|
||||
const [key, value] = multipartArgument.split("=", 2);
|
||||
multipartUploads[key] = value;
|
||||
});
|
||||
}
|
||||
if (cookieString) {
|
||||
let cookieParseOptions = {
|
||||
decode: function (s) {
|
||||
return s;
|
||||
}
|
||||
};
|
||||
// separate out cookie headers into separate data structure
|
||||
// note: cookie is case insensitive
|
||||
cookies = cookie.parse(
|
||||
cookieString.replace(/^Cookie: /gi, ""),
|
||||
cookieParseOptions
|
||||
);
|
||||
}
|
||||
let method;
|
||||
if (parsedArguments.X === "POST") {
|
||||
method = "post";
|
||||
} else if (parsedArguments.X === "PUT" || parsedArguments["T"]) {
|
||||
method = "put";
|
||||
} else if (parsedArguments.X === "PATCH") {
|
||||
method = "patch";
|
||||
} else if (parsedArguments.X === "DELETE") {
|
||||
method = "delete";
|
||||
} else if (parsedArguments.X === "OPTIONS") {
|
||||
method = "options";
|
||||
} else if (
|
||||
(parsedArguments["d"] ||
|
||||
parsedArguments["data"] ||
|
||||
parsedArguments["data-ascii"] ||
|
||||
parsedArguments["data-binary"] ||
|
||||
parsedArguments["F"] ||
|
||||
parsedArguments["form"]) &&
|
||||
!(parsedArguments["G"] || parsedArguments["get"])
|
||||
) {
|
||||
method = "post";
|
||||
} else if (parsedArguments["I"] || parsedArguments["head"]) {
|
||||
method = "head";
|
||||
} else {
|
||||
method = "get";
|
||||
}
|
||||
|
||||
let compressed = !!parsedArguments.compressed;
|
||||
let urlObject = URL.parse(url); // eslint-disable-line
|
||||
|
||||
// if GET request with data, convert data to query string
|
||||
// NB: the -G flag does not change the http verb. It just moves the data into the url.
|
||||
if (parsedArguments["G"] || parsedArguments["get"]) {
|
||||
urlObject.query = urlObject.query ? urlObject.query : "";
|
||||
let option =
|
||||
"d" in parsedArguments ? "d" : "data" in parsedArguments ? "data" : null;
|
||||
if (option) {
|
||||
let urlQueryString = "";
|
||||
|
||||
if (!url.includes("?")) {
|
||||
url += "?";
|
||||
} else {
|
||||
urlQueryString += "&";
|
||||
}
|
||||
|
||||
if (typeof parsedArguments[option] === "object") {
|
||||
urlQueryString += parsedArguments[option].join("&");
|
||||
} else {
|
||||
urlQueryString += parsedArguments[option];
|
||||
}
|
||||
urlObject.query += urlQueryString;
|
||||
url += urlQueryString;
|
||||
delete parsedArguments[option];
|
||||
}
|
||||
}
|
||||
let query = querystring.parse(urlObject.query, null, null, {
|
||||
maxKeys: 10000
|
||||
});
|
||||
|
||||
urlObject.search = null; // Clean out the search/query portion.
|
||||
let request = {
|
||||
url: url,
|
||||
urlWithoutQuery: URL.format(urlObject)
|
||||
};
|
||||
if (compressed) {
|
||||
request["compressed"] = true;
|
||||
}
|
||||
|
||||
if (Object.keys(query).length > 0) {
|
||||
request.query = query;
|
||||
}
|
||||
if (headers) {
|
||||
request.headers = headers;
|
||||
}
|
||||
request["method"] = method;
|
||||
|
||||
if (cookies) {
|
||||
request.cookies = cookies;
|
||||
request.cookieString = cookieString.replace("Cookie: ", "");
|
||||
}
|
||||
if (multipartUploads) {
|
||||
request.multipartUploads = multipartUploads;
|
||||
}
|
||||
if (parsedArguments.data) {
|
||||
request.data = parsedArguments.data;
|
||||
} else if (parsedArguments["data-binary"]) {
|
||||
request.data = parsedArguments["data-binary"];
|
||||
request.isDataBinary = true;
|
||||
} else if (parsedArguments["d"]) {
|
||||
request.data = parsedArguments["d"];
|
||||
} else if (parsedArguments["data-ascii"]) {
|
||||
request.data = parsedArguments["data-ascii"];
|
||||
}
|
||||
|
||||
if (parsedArguments["u"]) {
|
||||
request.auth = parsedArguments["u"];
|
||||
}
|
||||
if (parsedArguments["user"]) {
|
||||
request.auth = parsedArguments["user"];
|
||||
}
|
||||
if (Array.isArray(request.data)) {
|
||||
request.dataArray = request.data;
|
||||
request.data = joinDataArguments(request.data);
|
||||
}
|
||||
|
||||
if (parsedArguments["k"] || parsedArguments["insecure"]) {
|
||||
request.insecure = true;
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
export default parseCurlCommand;
|
||||
52
assets/js/pwa.js
Normal file
@@ -0,0 +1,52 @@
|
||||
export default () => {
|
||||
//*** Determine whether or not the PWA has been installed. ***//
|
||||
|
||||
// Step 1: Check local storage
|
||||
let pwaInstalled = localStorage.getItem('pwaInstalled') === 'yes';
|
||||
|
||||
// Step 2: Check if the display-mode is standalone. (Only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.matchMedia('(display-mode: standalone)').matches) {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
pwaInstalled = true;
|
||||
}
|
||||
|
||||
// Step 3: Check if the navigator is in standalone mode. (Again, only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.navigator.standalone === true) {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
pwaInstalled = true;
|
||||
}
|
||||
|
||||
//*** If the PWA has not been installed, show the install PWA prompt.. ***//
|
||||
let deferredPrompt = null;
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
deferredPrompt = event;
|
||||
|
||||
// Show the install button if the prompt appeared.
|
||||
if (!pwaInstalled) {
|
||||
document.querySelector('#installPWA').style.display = 'inline-flex';
|
||||
}
|
||||
});
|
||||
|
||||
// When the app is installed, remove install prompts.
|
||||
window.addEventListener('appinstalled', (event) => {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
pwaInstalled = true;
|
||||
document.getElementById('installPWA').style.display = 'none';
|
||||
});
|
||||
|
||||
// When the app is uninstalled, add the prompts back
|
||||
return async () => {
|
||||
if (deferredPrompt) {
|
||||
deferredPrompt.prompt();
|
||||
let outcome = await deferredPrompt.userChoice;
|
||||
|
||||
if (outcome === 'accepted') {
|
||||
console.log('Postwoman was installed successfully.')
|
||||
} else {
|
||||
console.log('Postwoman could not be installed. (Installation rejected by user.)')
|
||||
}
|
||||
deferredPrompt = null;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
58
build.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
const {
|
||||
spawnSync
|
||||
} = require("child_process");
|
||||
const runCommand = (command, args) =>
|
||||
spawnSync(command, args)
|
||||
.stdout.toString()
|
||||
.replace(/\n/g, "");
|
||||
|
||||
const FAIL_ON_ERROR = false;
|
||||
const PW_BUILD_DATA_DIR = "./.postwoman";
|
||||
const IS_DEV_MODE = process.argv.includes("--dev");
|
||||
|
||||
try {
|
||||
(async () => {
|
||||
// Create the build data directory if it does not exist.
|
||||
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
|
||||
fs.mkdirSync(PW_BUILD_DATA_DIR);
|
||||
}
|
||||
|
||||
let version = {};
|
||||
// Get the current version name as the tag from Git.
|
||||
version.name = process.env.TRAVIS_TAG || runCommand("git", ["tag"]);
|
||||
|
||||
// FALLBACK: If version.name was unset, let's grab it from GitHub.
|
||||
if (!version.name) {
|
||||
version.name = (await axios
|
||||
.get("https://api.github.com/repos/liyasthomas/postwoman/releases")
|
||||
// If we can't get it from GitHub, we'll resort to getting it from package.json
|
||||
.catch(ex => ({
|
||||
data: [{
|
||||
tag_name: require("./package.json").version
|
||||
}]
|
||||
}))).data[0]["tag_name"];
|
||||
}
|
||||
|
||||
// Get the current version hash as the short hash from Git.
|
||||
version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"]);
|
||||
// Get the 'variant' name as the branch, if it's not master.
|
||||
version.variant =
|
||||
process.env.TRAVIS_BRANCH ||
|
||||
runCommand("git", ["branch"])
|
||||
.split("* ")[1]
|
||||
.split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "");
|
||||
if (["", "master"].includes(version.variant))
|
||||
delete version.variant;
|
||||
|
||||
// Write version data into a file
|
||||
fs.writeFileSync(
|
||||
PW_BUILD_DATA_DIR + "/version.json",
|
||||
JSON.stringify(version)
|
||||
);
|
||||
})();
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
process.exit(FAIL_ON_ERROR ? 1 : 0);
|
||||
}
|
||||
7
components/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# COMPONENTS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
The components directory contains your Vue.js Components.
|
||||
|
||||
_Nuxt.js doesn't supercharge these components._
|
||||
210
components/autocomplete.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="autocomplete-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
v-model="value"
|
||||
@input="updateSuggestions"
|
||||
@keyup="updateSuggestions"
|
||||
@click="updateSuggestions"
|
||||
@keydown="handleKeystroke"
|
||||
ref="acInput"
|
||||
:spellcheck="spellcheck"
|
||||
:autocapitalize="spellcheck"
|
||||
:autocorrect="spellcheck"
|
||||
/>
|
||||
<ul
|
||||
class="suggestions"
|
||||
v-if="suggestions.length > 0 && suggestionsVisible"
|
||||
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }"
|
||||
>
|
||||
<li
|
||||
v-for="(suggestion, index) in suggestions"
|
||||
@click.prevent="forceSuggestion(suggestion)"
|
||||
:class="{ active: currentSuggestionIndex === index }"
|
||||
:key="index"
|
||||
>{{ suggestion }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
|
||||
input:focus + ul.suggestions,
|
||||
ul.suggestions:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.suggestions {
|
||||
display: none;
|
||||
background-color: var(--atc-color);
|
||||
position: absolute;
|
||||
top: calc(100% - 4px);
|
||||
margin: 0 4px;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
border-radius: 0 0 4px 4px;
|
||||
z-index: 9999;
|
||||
transition: transform 200ms ease-out;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 8px 16px;
|
||||
font-size: 18px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const KEY_TAB = 9;
|
||||
const KEY_ESC = 27;
|
||||
|
||||
const KEY_ARROW_UP = 38;
|
||||
const KEY_ARROW_DOWN = 40;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "Start typing...",
|
||||
required: false
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit("input", this.value);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
value: "application/json",
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
suggestionsVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateSuggestions(event) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if (event.which && event.which === KEY_ESC) {
|
||||
event.preventDefault();
|
||||
this.suggestionsVisible = false;
|
||||
this.currentSuggestionIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// As suggestions is a reactive property, this implicitly
|
||||
// causes suggestions to update.
|
||||
this.selectionStart = this.$refs.acInput.selectionStart;
|
||||
this.suggestionsOffsetLeft = 12 * this.selectionStart;
|
||||
this.suggestionsVisible = true;
|
||||
},
|
||||
|
||||
forceSuggestion(text) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + text;
|
||||
|
||||
this.selectionStart = this.value.length;
|
||||
this.suggestionsVisible = true;
|
||||
this.currentSuggestionIndex = -1;
|
||||
},
|
||||
|
||||
handleKeystroke(event) {
|
||||
switch (event.which) {
|
||||
case KEY_ARROW_UP:
|
||||
event.preventDefault();
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex - 1 >= 0
|
||||
? this.currentSuggestionIndex - 1
|
||||
: 0;
|
||||
break;
|
||||
|
||||
case KEY_ARROW_DOWN:
|
||||
event.preventDefault();
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex < this.suggestions.length - 1
|
||||
? this.currentSuggestionIndex + 1
|
||||
: this.suggestions.length - 1;
|
||||
break;
|
||||
|
||||
case KEY_TAB:
|
||||
event.preventDefault();
|
||||
let activeSuggestion = this.suggestions[
|
||||
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
|
||||
];
|
||||
if (activeSuggestion) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + activeSuggestion;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Gets the suggestions list to be displayed under the input box.
|
||||
*
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions() {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
|
||||
return (
|
||||
this.source
|
||||
.filter(entry => {
|
||||
return (
|
||||
entry.toLowerCase().startsWith(input.toLowerCase()) &&
|
||||
input.toLowerCase() !== entry.toLowerCase()
|
||||
);
|
||||
})
|
||||
// Cut off the part that's already been typed.
|
||||
.map(entry => entry.substring(this.selectionStart))
|
||||
// We only want the top 3 suggestions.
|
||||
.slice(0, 3)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.updateSuggestions({
|
||||
target: this.$refs.acInput
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
64
components/collections/addCollection.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">New Collection</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" placeholder="My New Collection" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addNewCollection">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Create</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
addNewCollection() {
|
||||
this.$store.commit("postwoman/addNewCollection", {
|
||||
name: this.$data.name
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
67
components/collections/addFolder.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="show = false">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">New Folder</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" placeholder="My New Folder" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addNewFolder">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Create</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collection: Object,
|
||||
collectionIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
addNewFolder() {
|
||||
this.$store.commit("postwoman/addNewFolder", {
|
||||
folder: { name: this.$data.name },
|
||||
collectionIndex: this.$props.collectionIndex
|
||||
});
|
||||
this.hideModal();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
102
components/collections/collection.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{collection.name}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeCollection" v-tooltip="'Delete collection'">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('edit-collection')" v-tooltip="'Edit collection'">
|
||||
<i class="material-icons">create</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('add-folder')" v-tooltip="'New Folder'">
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="showChildren">
|
||||
<ul>
|
||||
<li v-for="(folder, index) in collection.folders" :key="folder.name">
|
||||
<folder
|
||||
v-bind:folder="folder"
|
||||
v-bind:folderIndex="index"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-on:edit-folder="editFolder(collectionIndex, folder, index)"
|
||||
v-on:edit-request="$emit('edit-request', $event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="(collection.folders.length === 0) && (collection.requests.length === 0)">
|
||||
<label>Collection is empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li v-for="(request, index) in collection.requests" :key="index">
|
||||
<request
|
||||
v-bind:request="request"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-bind:folder-index="-1"
|
||||
v-bind:request-index="index"
|
||||
v-on:edit-request="$emit('edit-request', { request, collectionIndex, folderIndex: undefined, requestIndex: index })"
|
||||
></request>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: flex;
|
||||
margin-left: 32px;
|
||||
border-left: 1px solid var(--brd-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import folder from "./folder";
|
||||
import request from "./request";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
folder,
|
||||
request
|
||||
},
|
||||
props: {
|
||||
collectionIndex: Number,
|
||||
collection: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
selectedFolder: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleShowChildren() {
|
||||
this.showChildren = !this.showChildren;
|
||||
},
|
||||
removeCollection() {
|
||||
if (!confirm("Are you sure you want to remove this collection?")) return;
|
||||
this.$store.commit("postwoman/removeCollection", {
|
||||
collectionIndex: this.collectionIndex
|
||||
});
|
||||
},
|
||||
editFolder(collectionIndex, folder, folderIndex) {
|
||||
this.$emit("edit-folder", { collectionIndex, folder, folderIndex });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
71
components/collections/editCollection.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModel">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Collection</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModel">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" v-bind:placeholder="editingCollection.name" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveCollection">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingCollection: Object,
|
||||
editingCollectionIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
saveCollection() {
|
||||
const collectionUpdated = {
|
||||
...this.$props.editingCollection,
|
||||
name: this.$data.name
|
||||
};
|
||||
this.$store.commit("postwoman/editCollection", {
|
||||
collection: collectionUpdated,
|
||||
collectionIndex: this.$props.editingCollectionIndex
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
hideModel() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
70
components/collections/editFolder.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="show = false">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Folder</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" v-bind:placeholder="folder.name" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="editFolder">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collection: Object,
|
||||
collectionIndex: Number,
|
||||
folder: Object,
|
||||
folderIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
editFolder() {
|
||||
this.$store.commit("postwoman/editFolder", {
|
||||
collectionIndex: this.$props.collectionIndex,
|
||||
folder: { ...this.$props.folder, name: this.$data.name },
|
||||
folderIndex: this.$props.folderIndex
|
||||
});
|
||||
this.hideModal();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
126
components/collections/editRequest.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Request</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectLabel">Label</label>
|
||||
<input type="text" id="selectLabel" v-model="requestUpdateData.name" :placeholder="request.name" />
|
||||
<label for="selectCollection">Collection</label>
|
||||
<select type="text" id="selectCollection" v-model="requestUpdateData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>Current Collection</option>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ collection.name }}</option>
|
||||
</select>
|
||||
<label for="selectFolder">Folder</label>
|
||||
<select type="text" id="selectFolder" v-model="requestUpdateData.folderIndex">
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in folders" :key="index" :value="index">{{ folder.name }}</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveRequest">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
request: Object,
|
||||
requestIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
requestUpdateData: {
|
||||
name: undefined,
|
||||
collectionIndex: undefined,
|
||||
folderIndex: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
"requestUpdateData.collectionIndex": function resetFolderIndex() {
|
||||
// if user choosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderIndex` won't be reseted
|
||||
this.$data.requestUpdateData.folderIndex = undefined;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
folders() {
|
||||
const userSelectedAnyCollection =
|
||||
this.$data.requestUpdateData.collectionIndex !== undefined;
|
||||
if (!userSelectedAnyCollection) return [];
|
||||
|
||||
return this.$store.state.postwoman.collections[
|
||||
this.$data.requestUpdateData.collectionIndex
|
||||
].folders;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveRequest() {
|
||||
const userSelectedAnyCollection =
|
||||
this.$data.requestUpdateData.collectionIndex !== undefined;
|
||||
|
||||
const requestUpdated = {
|
||||
...this.$props.request,
|
||||
name: this.$data.requestUpdateData.name || this.$props.request.name,
|
||||
collection: userSelectedAnyCollection
|
||||
? this.$data.requestUpdateData.collectionIndex
|
||||
: this.$props.collectionIndex,
|
||||
folder: this.$data.requestUpdateData.folderIndex
|
||||
};
|
||||
|
||||
// pass data separately to don't depend on request's collection, folder fields
|
||||
// probably, they should be deprecated because they don't describe request itself
|
||||
this.$store.commit("postwoman/editRequest", {
|
||||
requestOld: this.$props.request,
|
||||
requestOldCollectionIndex: this.$props.collectionIndex,
|
||||
requestOldFolderIndex: this.$props.folderIndex,
|
||||
requestOldIndex: this.$props.requestIndex,
|
||||
requestNew: requestUpdated,
|
||||
requestNewCollectionIndex: requestUpdated.collection,
|
||||
requestNewFolderIndex: requestUpdated.folder
|
||||
});
|
||||
|
||||
this.hideModal();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
90
components/collections/folder.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="toggleShowChildren">
|
||||
<i class="material-icons" v-show="!showChildren">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
|
||||
<i class="material-icons">folder_open</i>
|
||||
<span>{{folder.name}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeFolder" v-tooltip="'Delete folder'">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="editFolder" v-tooltip="'Edit folder'">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="showChildren">
|
||||
<ul>
|
||||
<li v-for="(request, index) in folder.requests" :key="index">
|
||||
<request
|
||||
v-bind:request="request"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-bind:folder-index="folderIndex"
|
||||
v-bind:request-index="index"
|
||||
v-on:edit-request="$emit('edit-request', { request, collectionIndex, folderIndex, requestIndex: index })"
|
||||
></request>
|
||||
</li>
|
||||
<li v-if="folder.requests.length === 0">
|
||||
<label>Folder is empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: flex;
|
||||
margin-left: 32px;
|
||||
border-left: 1px solid var(--brd-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import request from "./request";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
folder: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number
|
||||
},
|
||||
components: {
|
||||
request
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleShowChildren() {
|
||||
this.showChildren = !this.showChildren;
|
||||
},
|
||||
selectRequest(request) {
|
||||
this.$store.commit("postwoman/selectRequest", { request });
|
||||
},
|
||||
removeFolder() {
|
||||
if (!confirm("Are you sure you want to remove this folder?")) return;
|
||||
this.$store.commit("postwoman/removeFolder", {
|
||||
collectionIndex: this.collectionIndex,
|
||||
folderIndex: this.folderIndex
|
||||
});
|
||||
},
|
||||
editFolder() {
|
||||
this.$emit("edit-folder");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
125
components/collections/importExportCollections.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModel">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Import / Export Collections</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModel">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<textarea v-model="collectionJson" rows="8"></textarea>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="'Replace current'"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>Replace with JSON</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="'Preserve current'"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>Import from JSON</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon" @click="exportJSON" v-tooltip="'Download file'">
|
||||
<i class="material-icons">get_app</i>
|
||||
<span>Export to JSON</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
computed: {
|
||||
collectionJson() {
|
||||
return JSON.stringify(this.$store.state.postwoman.collections, null, 2);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModel() {
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
openDialogChooseFileToReplaceWith() {
|
||||
this.$refs.inputChooseFileToReplaceWith.click();
|
||||
},
|
||||
openDialogChooseFileToImportFrom() {
|
||||
this.$refs.inputChooseFileToImportFrom.click();
|
||||
},
|
||||
replaceWithJSON() {
|
||||
let reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let collections = JSON.parse(content);
|
||||
this.$store.commit("postwoman/replaceCollections", collections);
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let collections = JSON.parse(content);
|
||||
this.$store.commit("postwoman/importCollections", collections);
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.collectionJson;
|
||||
text = text.replace(/\n/g, "\r\n");
|
||||
let blob = new Blob([text], {
|
||||
type: "text/json"
|
||||
});
|
||||
let anchor = document.createElement("a");
|
||||
anchor.download = "postwoman-collection.json";
|
||||
anchor.href = window.URL.createObjectURL(blob);
|
||||
anchor.target = "_blank";
|
||||
anchor.style.display = "none";
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
185
components/collections/index.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<!--
|
||||
TODO:
|
||||
- probably refactor and pass event arguments to modals directly without unpacking
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="collections-wrapper">
|
||||
<addCollection v-bind:show="showModalAdd" v-on:hide-modal="displayModalAdd(false)"></addCollection>
|
||||
<editCollection
|
||||
v-bind:show="showModalEdit"
|
||||
v-bind:editingCollection="editingCollection"
|
||||
v-bind:editingCollectionIndex="editingCollectionIndex"
|
||||
v-on:hide-modal="displayModalEdit(false)"
|
||||
></editCollection>
|
||||
<addFolder
|
||||
v-bind:show="showModalAddFolder"
|
||||
v-bind:collection="editingCollection"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-on:hide-modal="displayModalAddFolder(false)"
|
||||
></addFolder>
|
||||
<editFolder
|
||||
v-bind:show="showModalEditFolder"
|
||||
v-bind:collection="editingCollection"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-bind:folder="editingFolder"
|
||||
v-bind:folderIndex="editingFolderIndex"
|
||||
v-on:hide-modal="displayModalEditFolder(false)"
|
||||
></editFolder>
|
||||
<editRequest
|
||||
v-bind:show="showModalEditRequest"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-bind:folderIndex="editingFolderIndex"
|
||||
v-bind:request="editingRequest"
|
||||
v-bind:requestIndex="editingRequestIndex"
|
||||
v-on:hide-modal="displayModalEditRequest(false)"
|
||||
></editRequest>
|
||||
<importExportCollections
|
||||
v-bind:show="showModalImportExport"
|
||||
v-on:hide-modal="displayModalImportExport(false)"
|
||||
></importExportCollections>
|
||||
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="displayModalAdd(true)">
|
||||
<i class="material-icons">add</i>
|
||||
<span>New</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="displayModalImportExport(true)">
|
||||
<i class="material-icons">import_export</i>
|
||||
<span>Import / Export</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li v-for="(collection, index) in collections" :key="collection.name">
|
||||
<collection
|
||||
v-bind:collection-index="index"
|
||||
v-bind:collection="collection"
|
||||
v-on:edit-collection="editCollection(collection, index)"
|
||||
v-on:add-folder="addFolder(collection, index)"
|
||||
v-on:edit-folder="editFolder($event)"
|
||||
v-on:edit-request="editRequest($event)"
|
||||
></collection>
|
||||
</li>
|
||||
<li v-if="collections.length === 0">
|
||||
<label>Collections are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import addCollection from "./addCollection";
|
||||
import addFolder from "./addFolder";
|
||||
import collection from "./collection";
|
||||
import editCollection from "./editCollection";
|
||||
import editFolder from "./editFolder";
|
||||
import editRequest from "./editRequest";
|
||||
import importExportCollections from "./importExportCollections";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addCollection,
|
||||
addFolder,
|
||||
collection,
|
||||
editCollection,
|
||||
editFolder,
|
||||
editRequest,
|
||||
importExportCollections
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModalAdd: false,
|
||||
showModalEdit: false,
|
||||
showModalImportExport: false,
|
||||
showModalAddFolder: false,
|
||||
showModalEditFolder: false,
|
||||
showModalEditRequest: false,
|
||||
editingCollection: undefined,
|
||||
editingCollectionIndex: undefined,
|
||||
editingFolder: undefined,
|
||||
editingFolderIndex: undefined,
|
||||
editingRequest: undefined,
|
||||
editingRequestIndex: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
collections() {
|
||||
return this.$store.state.postwoman.collections;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay;
|
||||
},
|
||||
displayModalEdit(shouldDisplay) {
|
||||
this.showModalEdit = shouldDisplay;
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData();
|
||||
},
|
||||
displayModalImportExport(shouldDisplay) {
|
||||
this.showModalImportExport = shouldDisplay;
|
||||
},
|
||||
displayModalAddFolder(shouldDisplay) {
|
||||
this.showModalAddFolder = shouldDisplay;
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData();
|
||||
},
|
||||
displayModalEditFolder(shouldDisplay) {
|
||||
this.showModalEditFolder = shouldDisplay;
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData();
|
||||
},
|
||||
displayModalEditRequest(shouldDisplay) {
|
||||
this.showModalEditRequest = shouldDisplay;
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData();
|
||||
},
|
||||
editCollection(collection, collectionIndex) {
|
||||
this.$data.editingCollection = collection;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.displayModalEdit(true);
|
||||
},
|
||||
addFolder(collection, collectionIndex) {
|
||||
this.$data.editingCollection = collection;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.displayModalAddFolder(true);
|
||||
},
|
||||
editFolder(payload) {
|
||||
const { collectionIndex, folder, folderIndex } = payload;
|
||||
this.$data.editingCollection = collection;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.$data.editingFolder = folder;
|
||||
this.$data.editingFolderIndex = folderIndex;
|
||||
this.displayModalEditFolder(true);
|
||||
},
|
||||
editRequest(payload) {
|
||||
const { request, collectionIndex, folderIndex, requestIndex } = payload;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.$data.editingFolderIndex = folderIndex;
|
||||
this.$data.editingRequest = request;
|
||||
this.$data.editingRequestIndex = requestIndex;
|
||||
this.displayModalEditRequest(true);
|
||||
},
|
||||
resetSelectedData() {
|
||||
this.$data.editingCollection = undefined;
|
||||
this.$data.editingCollectionIndex = undefined;
|
||||
this.$data.editingFolder = undefined;
|
||||
this.$data.editingFolderIndex = undefined;
|
||||
this.$data.editingRequest = undefined;
|
||||
this.$data.editingRequestIndex = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
55
components/collections/request.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="selectRequest()" v-tooltip="'Use request'">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
<span>{{request.name}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeRequest" v-tooltip="'Delete request'">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('edit-request')" v-tooltip="'Edit request'">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: flex;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--brd-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
request: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
requestIndex: Number
|
||||
},
|
||||
methods: {
|
||||
selectRequest() {
|
||||
this.$store.commit("postwoman/selectRequest", { request: this.request });
|
||||
},
|
||||
removeRequest() {
|
||||
if (!confirm("Are you sure you want to remove this request?")) return;
|
||||
this.$store.commit("postwoman/removeRequest", {
|
||||
collectionIndex: this.collectionIndex,
|
||||
folderIndex: this.folderIndex,
|
||||
requestIndex: this.requestIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
164
components/collections/saveRequestAs.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Save Request As</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectLabel">Label</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="requestData.name"
|
||||
v-bind:placeholder="defaultRequestName"
|
||||
/>
|
||||
<label for="selectCollection">Collection</label>
|
||||
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>Select a Collection</option>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ collection.name }}</option>
|
||||
</select>
|
||||
<label for="selectFolder">Folder</label>
|
||||
<select type="text" id="selectFolder" v-model="requestData.folderIndex">
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in folders" :key="index" :value="index">{{ folder.name }}</option>
|
||||
</select>
|
||||
<label for="selectRequest">Request</label>
|
||||
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option
|
||||
v-for="(folder, index) in requests"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ folder.name }}</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveRequestAs">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingRequest: Object
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRequestName: "My New Request",
|
||||
requestData: {
|
||||
name: undefined,
|
||||
collectionIndex: undefined,
|
||||
folderIndex: undefined,
|
||||
requestIndex: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user choosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderIndex` won't be reseted
|
||||
this.$data.requestData.folderIndex = undefined;
|
||||
this.$data.requestData.requestIndex = undefined;
|
||||
},
|
||||
"requestData.folderIndex": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
folders() {
|
||||
const userSelectedAnyCollection =
|
||||
this.$data.requestData.collectionIndex !== undefined;
|
||||
if (!userSelectedAnyCollection) return [];
|
||||
|
||||
return this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
].folders;
|
||||
},
|
||||
requests() {
|
||||
const userSelectedAnyCollection =
|
||||
this.$data.requestData.collectionIndex !== undefined;
|
||||
if (!userSelectedAnyCollection) return [];
|
||||
|
||||
const userSelectedAnyFolder =
|
||||
this.$data.requestData.folderIndex !== undefined;
|
||||
if (userSelectedAnyFolder) {
|
||||
const collection = this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
];
|
||||
const folder = collection.folders[this.$data.requestData.folderIndex];
|
||||
const requests = folder.requests;
|
||||
return requests;
|
||||
} else {
|
||||
const collection = this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
];
|
||||
const requests = collection.requests;
|
||||
return requests;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveRequestAs() {
|
||||
const userDidntSpecifyCollection =
|
||||
this.$data.requestData.collectionIndex === undefined;
|
||||
if (userDidntSpecifyCollection) {
|
||||
this.$toast.error("Select a Collection", {
|
||||
icon: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const requestUpdated = {
|
||||
...this.$props.editingRequest,
|
||||
name: this.$data.requestData.name || this.$data.defaultRequestName,
|
||||
collection: this.$data.requestData.collectionIndex
|
||||
};
|
||||
|
||||
this.$store.commit("postwoman/saveRequestAs", {
|
||||
request: requestUpdated,
|
||||
collectionIndex: this.$data.requestData.collectionIndex,
|
||||
folderIndex: this.$data.requestData.folderIndex,
|
||||
requestIndex: this.$data.requestData.requestIndex
|
||||
});
|
||||
|
||||
this.hideModal();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
this.$emit("hide-model"); // for backward compatibility // TODO: use fixed event
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
332
components/history.vue
Normal file
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<pw-section class="green" icon="history" label="History" ref="history">
|
||||
<ul>
|
||||
<li id="filter-history">
|
||||
<input
|
||||
aria-label="Search"
|
||||
type="text"
|
||||
placeholder="search history"
|
||||
:readonly="history.length === 0"
|
||||
v-model="filterText"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li></li>
|
||||
<li @click="sort_by_label()">
|
||||
<label>
|
||||
Label
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_time()">
|
||||
<label>
|
||||
Time
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_status_code()">
|
||||
<label>
|
||||
Status
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_url()">
|
||||
<label>
|
||||
URL
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_path()">
|
||||
<label>
|
||||
Path
|
||||
</label>
|
||||
</li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{filled: filteredHistory.length}"
|
||||
:size="54"
|
||||
:remain="Math.min(5, filteredHistory.length)"
|
||||
>
|
||||
<ul v-for="(entry, index) in filteredHistory" :key="index" class="entry">
|
||||
<li>
|
||||
<button v-if="entry.usesScripts"
|
||||
v-tooltip="'This entry used pre-request scripts'"
|
||||
class="icon"
|
||||
>
|
||||
<i class="material-icons">code</i>
|
||||
</button>
|
||||
<button v-else
|
||||
v-tooltip="'No pre-request scripts'"
|
||||
class="icon"
|
||||
>
|
||||
<i class="material-icons">http</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
aria-label="Label"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.label"
|
||||
placeholder="No label"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="Time" type="text" readonly :value="entry.time" v-tooltip="entry.date" />
|
||||
</li>
|
||||
<li class="method-list-item">
|
||||
<input
|
||||
aria-label="Method"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.method"
|
||||
:class="findEntryStatus(entry).className"
|
||||
:style="{'--status-code': entry.status}"
|
||||
/>
|
||||
<span
|
||||
class="entry-status-code"
|
||||
:class="findEntryStatus(entry).className"
|
||||
:style="{'--status-code': entry.status}"
|
||||
>{{entry.status}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="URL" type="text" readonly :value="entry.url" />
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="Path" type="text" readonly :value="entry.path" placeholder="No path" />
|
||||
</li>
|
||||
<div class="show-on-small-screen">
|
||||
<li>
|
||||
<button
|
||||
v-tooltip="'Delete entry'"
|
||||
class="icon"
|
||||
:id="'delete-button#'+index"
|
||||
@click="deleteHistory(entry)"
|
||||
aria-label="Delete"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
v-tooltip="'Edit entry'"
|
||||
class="icon"
|
||||
:id="'use-button#'+index"
|
||||
@click="useHistory(entry)"
|
||||
aria-label="Edit"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<ul :class="{hidden: filteredHistory.length != 0 || history.length === 0 }">
|
||||
<li>
|
||||
<label>Nothing found "{{filterText}}"</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="history.length === 0">
|
||||
<li>
|
||||
<label>History is empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="history.length !== 0">
|
||||
<li v-if="!isClearingHistory">
|
||||
<button
|
||||
class="icon"
|
||||
id="clear-history-button"
|
||||
:disabled="history.length === 0"
|
||||
@click="enableHistoryClearing"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
<span>Clear All</span>
|
||||
</button>
|
||||
</li>
|
||||
<li v-else>
|
||||
<div class="flex-wrap">
|
||||
<label for="clear-history-button">Are you sure?</label>
|
||||
<div>
|
||||
<button class="icon" id="confirm-clear-history-button" @click="clearHistory">Yes</button>
|
||||
<button class="icon" id="reject-clear-history-button" @click="disableHistoryClearing">No</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
[readonly] {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.virtual-list.filled {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import VirtualList from "vue-virtual-scroll-list";
|
||||
import section from "./section";
|
||||
import { findStatusGroup } from "../pages/index";
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property));
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section,
|
||||
VirtualList
|
||||
},
|
||||
data() {
|
||||
const localStorageHistory = JSON.parse(
|
||||
window.localStorage.getItem("history")
|
||||
);
|
||||
return {
|
||||
history: localStorageHistory || [],
|
||||
filterText: "",
|
||||
showFilter: false,
|
||||
isClearingHistory: false,
|
||||
reverse_sort_label: false,
|
||||
reverse_sort_time: false,
|
||||
reverse_sort_status_code: false,
|
||||
reverse_sort_url: false,
|
||||
reverse_sort_path: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredHistory() {
|
||||
return this.history.filter(entry => {
|
||||
const filterText = this.filterText.toLowerCase();
|
||||
return Object.keys(entry).some(key => {
|
||||
let value = entry[key];
|
||||
value = typeof value !== "string" ? value.toString() : value;
|
||||
return value.toLowerCase().includes(filterText);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearHistory() {
|
||||
this.history = [];
|
||||
this.filterText = "";
|
||||
this.disableHistoryClearing();
|
||||
updateOnLocalStorage("history", this.history);
|
||||
this.$toast.error("History Deleted", {
|
||||
icon: "delete"
|
||||
});
|
||||
},
|
||||
useHistory(entry) {
|
||||
this.$emit("useHistory", entry);
|
||||
},
|
||||
findEntryStatus(entry) {
|
||||
const foundStatusGroup = findStatusGroup(entry.status);
|
||||
return (
|
||||
foundStatusGroup || {
|
||||
className: ""
|
||||
}
|
||||
);
|
||||
},
|
||||
deleteHistory(entry) {
|
||||
this.history.splice(this.history.indexOf(entry), 1);
|
||||
if (this.history.length === 0) {
|
||||
this.filterText = "";
|
||||
}
|
||||
updateOnLocalStorage("history", this.history);
|
||||
this.$toast.error("Deleted", {
|
||||
icon: "delete"
|
||||
});
|
||||
},
|
||||
addEntry(entry) {
|
||||
this.history.push(entry);
|
||||
updateOnLocalStorage("history", this.history);
|
||||
},
|
||||
enableHistoryClearing() {
|
||||
if (!this.history || !this.history.length) return;
|
||||
this.isClearingHistory = true;
|
||||
},
|
||||
disableHistoryClearing() {
|
||||
this.isClearingHistory = false;
|
||||
},
|
||||
sort_by_time() {
|
||||
let byDate = this.history.slice(0);
|
||||
byDate.sort((a, b) => {
|
||||
let date_a = a.date.split("/");
|
||||
let date_b = b.date.split("/");
|
||||
let time_a = a.time.split(":");
|
||||
let time_b = b.time.split(":");
|
||||
let final_a = new Date(
|
||||
date_a[2],
|
||||
date_a[1],
|
||||
date_a[0],
|
||||
time_a[0],
|
||||
time_a[1],
|
||||
time_a[2]
|
||||
);
|
||||
let final_b = new Date(
|
||||
date_b[2],
|
||||
date_b[1],
|
||||
date_b[0],
|
||||
time_b[0],
|
||||
time_b[1],
|
||||
time_b[2]
|
||||
);
|
||||
if (this.reverse_sort_time) return final_b - final_a;
|
||||
else return final_a - final_b;
|
||||
});
|
||||
this.history = byDate;
|
||||
this.reverse_sort_time = !this.reverse_sort_time;
|
||||
},
|
||||
sort_by_status_code() {
|
||||
let byCode = this.history.slice(0);
|
||||
byCode.sort((a, b) => {
|
||||
if (this.reverse_sort_status_code) return b.status - a.status;
|
||||
else return a.status - b.status;
|
||||
});
|
||||
this.history = byCode;
|
||||
this.reverse_sort_status_code = !this.reverse_sort_status_code;
|
||||
},
|
||||
sort_by_url() {
|
||||
let byUrl = this.history.slice(0);
|
||||
byUrl.sort((a, b) => {
|
||||
if (this.reverse_sort_url)
|
||||
return a.url == b.url ? 0 : +(a.url < b.url) || -1;
|
||||
else return a.url == b.url ? 0 : +(a.url > b.url) || -1;
|
||||
});
|
||||
this.history = byUrl;
|
||||
this.reverse_sort_url = !this.reverse_sort_url;
|
||||
},
|
||||
sort_by_label() {
|
||||
let byLabel = this.history.slice(0);
|
||||
byLabel.sort((a, b) => {
|
||||
if (this.reverse_sort_label)
|
||||
return a.label == b.label ? 0 : +(a.label < b.label) || -1;
|
||||
else return a.label == b.label ? 0 : +(a.label > b.label) || -1;
|
||||
});
|
||||
this.history = byLabel;
|
||||
this.reverse_sort_label = !this.reverse_sort_label;
|
||||
},
|
||||
sort_by_path() {
|
||||
let byPath = this.history.slice(0);
|
||||
byPath.sort((a, b) => {
|
||||
if (this.reverse_sort_path)
|
||||
return a.path == b.path ? 0 : +(a.path < b.path) || -1;
|
||||
else return a.path == b.path ? 0 : +(a.path > b.path) || -1;
|
||||
});
|
||||
this.history = byPath;
|
||||
this.reverse_sort_path = !this.reverse_sort_path;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
56
components/logo.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 612.001 612.001"
|
||||
style="enable-background:new 0 0 612.001 612.001;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<defs id="defs11" />
|
||||
<g id="g3826" transform="translate(-516.40798,-163.88978)">
|
||||
<circle
|
||||
:fill="color"
|
||||
transform="scale(1,-1)"
|
||||
style="stroke-width:1.19531453"
|
||||
r="178.70923"
|
||||
cy="-501.55591"
|
||||
cx="822.40845"
|
||||
id="circle3814"
|
||||
/>
|
||||
<g id="g3820" transform="translate(516.40798,163.89028)">
|
||||
<g id="g3818">
|
||||
<path
|
||||
:fill="color"
|
||||
id="path3816"
|
||||
data-old_color="#121212"
|
||||
class="active-path"
|
||||
data-original="#121212"
|
||||
d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#circle3814 {
|
||||
/* fill: var(--fg-color); */
|
||||
fill: transparent;
|
||||
}
|
||||
/* #path3816 {
|
||||
fill: var(--bg-color);
|
||||
} */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
74
components/modal.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<transition name="modal-fade">
|
||||
<div class="modal-backdrop">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<style scoped>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
z-index: 998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
margin: 8px;
|
||||
padding: 12px;
|
||||
transition: all 0.2s ease;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0px 16px 70px;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="modal" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the modal transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.modal-fade-enter,
|
||||
.modal-fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-fade-enter .modal-container,
|
||||
.modal-fade-leave-active .modal-container {
|
||||
transform: scale(0.8);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
52
components/section.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': !frameColorsEnabled }">
|
||||
<legend @click.prevent="collapse">
|
||||
<span>{{ label }}</span>
|
||||
<i class="material-icons" v-if="isCollapsed">expand_more</i>
|
||||
<i class="material-icons" v-if="!isCollapsed">expand_less</i>
|
||||
</legend>
|
||||
<div class="collapsible" :class="{ hidden: collapsed }">
|
||||
<slot />
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
fieldset.no-colored-frames legend {
|
||||
color: var(--fg-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
frameColorsEnabled() {
|
||||
return this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false;
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: false
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "Section"
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
collapse({ target }) {
|
||||
const parent = target.parentNode.parentNode;
|
||||
parent.querySelector(".collapsible").classList.toggle("hidden");
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
68
components/settings/swatch.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="color" :data-color="color">
|
||||
<span :style="{backgroundColor: color}" class="preview">
|
||||
<i v-if="active" class="material-icons activeTick">done</i>
|
||||
</span>
|
||||
{{ name || color }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.color {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 16px 0 4px;
|
||||
margin: 4px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
|
||||
&.active {
|
||||
background-color: var(--bg-dark-color);
|
||||
}
|
||||
|
||||
.preview {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
|
||||
.activeTick {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color.vibrant {
|
||||
.preview .activeTick {
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
98
components/toggle.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div @click="toggle()">
|
||||
<label class="toggle" :class="{on: on}" ref="toggle">
|
||||
<span class="handle"></span>
|
||||
</label>
|
||||
<label class="caption">
|
||||
<slot />
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$useBorder: false;
|
||||
$borderColor: var(--fg-light-color);
|
||||
$activeColor: var(--ac-color);
|
||||
$inactiveColor: var(--fg-light-color);
|
||||
|
||||
$inactiveHandleColor: var(--bg-color);
|
||||
$activeHandleColor: var(--act-color);
|
||||
|
||||
$width: 32px;
|
||||
$height: 16px;
|
||||
$handleSpacing: 4px;
|
||||
|
||||
$transition: all 0.2s ease-in-out;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.caption {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: $width;
|
||||
height: $height;
|
||||
border: if($useBorder, 2px solid $borderColor, none);
|
||||
background-color: if($useBorder, transparent, $inactiveColor);
|
||||
vertical-align: middle;
|
||||
|
||||
border-radius: 32px;
|
||||
transition: $transition;
|
||||
box-sizing: initial;
|
||||
padding: 0;
|
||||
margin: 8px 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.handle {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: $handleSpacing;
|
||||
background-color: $inactiveHandleColor;
|
||||
|
||||
width: #{$height - ($handleSpacing * 2)};
|
||||
height: #{$height - ($handleSpacing * 2)};
|
||||
border-radius: 100px;
|
||||
|
||||
pointer-events: none;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
&.on {
|
||||
background-color: $activeColor;
|
||||
border-color: $activeColor;
|
||||
|
||||
.handle {
|
||||
background-color: $activeHandleColor;
|
||||
left: #{$width - $height};
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
on: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggle() {
|
||||
const containsOnClass = this.$refs.toggle.classList.toggle("on");
|
||||
this.$emit("change", containsOnClass);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
9
cypress.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"integrationFolder": "tests/e2e/integration",
|
||||
"screenshotsFolder": "tests/e2e/screenshots",
|
||||
"fixturesFolder": "tests/e2e/fixtures",
|
||||
"supportFile": "tests/e2e/support",
|
||||
"pluginsFile": false,
|
||||
"video": false
|
||||
}
|
||||
7
database.rules.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
|
||||
"rules": {
|
||||
".read": false,
|
||||
".write": false
|
||||
}
|
||||
}
|
||||
8
directives/textareaAutoHeight.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
name: "textareaAutoHeight",
|
||||
update(element) {
|
||||
if (element.scrollHeight !== element.clientHeight) {
|
||||
element.style.minHeight = `${element.scrollHeight}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
- "./.postwoman:/app/.postwoman"
|
||||
- "./assets:/app/assets"
|
||||
- "./directives:/app/directives"
|
||||
- "./layouts:/app/layouts"
|
||||
- "./middleware:/app/middleware"
|
||||
- "./pages:/app/pages"
|
||||
- "./plugins:/app/plugins"
|
||||
- "./static:/app/static"
|
||||
- "./store:/app/store"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: "npm run dev"
|
||||
BIN
favicon.ico
|
Before Width: | Height: | Size: 15 KiB |
22
firebase.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"database": {
|
||||
"rules": "database.rules.json"
|
||||
},
|
||||
"firestore": {
|
||||
"rules": "firestore.rules",
|
||||
"indexes": "firestore.indexes.json"
|
||||
},
|
||||
"hosting": {
|
||||
"target": "postwoman",
|
||||
"public": "dist",
|
||||
"cleanUrls": true,
|
||||
"ignore": [
|
||||
"firebase.json",
|
||||
"**/.*",
|
||||
"**/node_modules/**"
|
||||
]
|
||||
},
|
||||
"storage": {
|
||||
"rules": "storage.rules"
|
||||
}
|
||||
}
|
||||
26
firestore.indexes.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
// Example:
|
||||
//
|
||||
// "indexes": [
|
||||
// {
|
||||
// "collectionGroup": "widgets",
|
||||
// "queryScope": "COLLECTION",
|
||||
// "fields": [
|
||||
// { "fieldPath": "foo", "arrayConfig": "CONTAINS" },
|
||||
// { "fieldPath": "bar", "mode": "DESCENDING" }
|
||||
// ]
|
||||
// },
|
||||
//
|
||||
// "fieldOverrides": [
|
||||
// {
|
||||
// "collectionGroup": "widgets",
|
||||
// "fieldPath": "baz",
|
||||
// "indexes": [
|
||||
// { "order": "ASCENDING", "queryScope": "COLLECTION" }
|
||||
// ]
|
||||
// },
|
||||
// ]
|
||||
// ]
|
||||
"indexes": [],
|
||||
"fieldOverrides": []
|
||||
}
|
||||
7
firestore.rules
Normal file
@@ -0,0 +1,7 @@
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
match /{document=**} {
|
||||
allow read, write;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
functions/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
8
functions/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const functions = require('firebase-functions');
|
||||
|
||||
// // Create and Deploy Your First Cloud Functions
|
||||
// // https://firebase.google.com/docs/functions/write-firebase-functions
|
||||
//
|
||||
// exports.helloWorld = functions.https.onRequest((request, response) => {
|
||||
// response.send("Hello from Firebase!");
|
||||
// });
|
||||
1915
functions/package-lock.json
generated
Normal file
22
functions/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase serve --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "8"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase-admin": "^8.0.0",
|
||||
"firebase-functions": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^0.1.6"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
17
functions/preRequest.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export default function getEnvironmentVariablesFromScript(script) {
|
||||
let _variables = {};
|
||||
|
||||
// the pw object is the proxy by which pre-request scripts can pass variables to the request.
|
||||
// for security and control purposes, this is the only way a pre-request script should modify variables.
|
||||
let pw = {
|
||||
environment: {
|
||||
set: (key, value) => _variables[key] = value,
|
||||
},
|
||||
// globals that the script is allowed to have access to.
|
||||
};
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
(new Function('pw', script))(pw);
|
||||
|
||||
return _variables;
|
||||
}
|
||||
7
functions/templating.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function parseTemplateString(string, variables) {
|
||||
if(!variables || !string) {
|
||||
return string;
|
||||
}
|
||||
const searchTerm = /<<([^>]*)>>/g; // "<<myVariable>>"
|
||||
return string.replace(searchTerm, (match, p1) => variables[p1] || '');
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 612.001 612.001" style="enable-background:new 0 0 612.001 612.001;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
|
||||
<path d="M64.601,236.822c0,157.434,128.185,375.178,241.4,375.178c106.581,0,241.399-217.744,241.399-375.178S439.322,0,306,0 S64.601,79.388,64.601,236.822z M368.721,353.237c29.475-29.475,70.598-40.195,108.552-32.173 c8.021,37.954-2.698,79.077-32.173,108.552c-29.475,29.475-70.598,40.195-108.552,32.173 C328.526,423.834,339.246,382.711,368.721,353.237z M134.727,321.063c37.954-8.021,79.077,2.698,108.552,32.173 c29.475,29.475,40.195,70.598,32.173,108.552c-37.954,8.021-79.077-2.698-108.552-32.173 C137.425,400.139,126.706,359.017,134.727,321.063z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#51FF0D"/>
|
||||
</g></g> </svg>
|
||||
|
Before Width: | Height: | Size: 952 B |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 82 KiB |
276
index.html
@@ -1,276 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
|
||||
<title>Postwoman - API request builder</title>
|
||||
<meta name="description" content="API request builder">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<meta itemprop="name" content="Postwoman">
|
||||
<meta itemprop="description" content="API request builder">
|
||||
<meta itemprop="image" content="icons/icon-192x192.png">
|
||||
<!-- See https://goo.gl/OOhYW5 -->
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<!-- See https://goo.gl/qRE0vM -->
|
||||
<meta name="theme-color" content="#121212">
|
||||
<!-- Add to homescreen for Chrome on Android. Fallback for manifest.json -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="application-name" content="Postwoman">
|
||||
<!-- Add to homescreen for Safari on iOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Postwoman">
|
||||
<!-- Homescreen icons -->
|
||||
<link rel="apple-touch-icon" href="icons/icon-48x48.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="icons/icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="96x96" href="icons/icon-96x96.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="icons/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icons/icon-192x192.png">
|
||||
<!-- Tile icon for Windows 8 (144x144 + tile color) -->
|
||||
<meta name="msapplication-TileImage" content="icons/icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#121212">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:site_name" content="Postwoman">
|
||||
<meta property="og:url" content="https://Postwoman.ml">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="Postwoman">
|
||||
<meta property="og:description" content="API request builder">
|
||||
<meta property="og:image" content="icons/icon-144x144.png">
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@liyasthomas">
|
||||
<meta name="twitter:creator" content="@liyasthomas">
|
||||
<meta name="twitter:url" content="https://Postwoman.ml">
|
||||
<meta name="twitter:title" content="Postwoman">
|
||||
<meta name="twitter:description" content="API request builder">
|
||||
<meta name="twitter:image" content="icons/icon-144x144.png">
|
||||
<!-- Web Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:500,700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('sw.js').then(function(registration) {
|
||||
console.log('ServiceWorker registration successful with scope: ', registration.scope);
|
||||
}).catch(function(err) {
|
||||
console.log('ServiceWorker registration failed: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main id="app">
|
||||
<header>
|
||||
<div>
|
||||
<a href="">
|
||||
<h1 class="logo"><img src="icons/logo.svg" alt="" style="height: 24px; margin-right: 16px">Postwoman</h1>
|
||||
</a>
|
||||
<h3>API request builder</h3>
|
||||
</div>
|
||||
</header>
|
||||
<fieldset class="request">
|
||||
<legend v-on:click="collapse">Request ⭥</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="method">Method</label>
|
||||
<select v-model="method">
|
||||
<option>GET</option>
|
||||
<option>POST</option>
|
||||
<option>PUT</option>
|
||||
<option>DELETE</option>
|
||||
<option>OPTIONS</option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input v-model="url">
|
||||
</li>
|
||||
<li>
|
||||
<label for="path">Path</label>
|
||||
<input v-model="path">
|
||||
</li>
|
||||
<li>
|
||||
<label for="action"> </label>
|
||||
<button name="action" @click="sendRequest">Send</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="reqbody" v-if="method === 'POST' || method === 'PUT'">
|
||||
<legend v-on:click="collapse">Request Body ⭥</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label>Content Type</label>
|
||||
<select v-model="contentType">
|
||||
<option>application/json</option>
|
||||
<option>www-form/urlencoded</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ol v-for="(param, index) in bodyParams">
|
||||
<li>
|
||||
<label :for="'bparam'+index">Key {{index + 1}}</label>
|
||||
<input :name="'bparam'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'bvalue'+index">Value {{index + 1}}</label>
|
||||
<input :name="'bvalue'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="request"> </label>
|
||||
<button name="request" @click="removeRequestBodyParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="addrequest">Action</label>
|
||||
<button name="addrequest" @click="addRequestBodyParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{rawRequestBody || '(add atleast one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="authentication hidden">
|
||||
<legend v-on:click="collapse">Authentication ⭥</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="auth">Authentication Type</label>
|
||||
<select v-model="auth">
|
||||
<option>None</option>
|
||||
<option>Basic</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="auth === 'Basic'">
|
||||
<li>
|
||||
<label for="http_basic_user">User</label>
|
||||
<input v-model="httpUser">
|
||||
</li>
|
||||
<li>
|
||||
<label for="http_basic_passwd">Password</label>
|
||||
<input v-model="httpPassword" type="password">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="parameters hidden">
|
||||
<legend v-on:click="collapse">Parameters ⭥</legend>
|
||||
<div class="collapsible">
|
||||
<ol v-for="(param, index) in params">
|
||||
<li>
|
||||
<label :for="'param'+index">Key {{index + 1}}</label>
|
||||
<input :name="'param'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'value'+index">Value {{index + 1}}</label>
|
||||
<input :name="'value'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="param"> </label>
|
||||
<button name="param" @click="removeRequestParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="add">Action</label>
|
||||
<button name="add" @click="addRequestParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{queryString || '(add atleast one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="response" id="response" ref="response">
|
||||
<legend v-on:click="collapse">Response ⭥</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="status">status</label>
|
||||
<input name="status" type="text" readonly :value="response.status || '(waiting to send request)'">
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(value, key) in response.headers">
|
||||
<li>
|
||||
<label for="value">{{key}}</label>
|
||||
<input name="value" :value="value" readonly>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="body">response</label>
|
||||
<textarea name="body" rows="6" readonly>{{response.body || '(waiting to send request)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<footer>
|
||||
<a href="https://github.com/liyasthomas/postwoman"><img src="icons/github.svg" alt="" style="margin-right: 16px">GitHub</a>
|
||||
<button id="installPWA" onclick="installPWA()">
|
||||
Install PWA
|
||||
</button>
|
||||
</footer>
|
||||
</main>
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
let pwaInstalled = localStorage.getItem('pwaInstalled') == 'yes'
|
||||
if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||
localStorage.setItem('pwaInstalled', 'yes')
|
||||
pwaInstalled = true
|
||||
}
|
||||
if (window.navigator.standalone === true) {
|
||||
localStorage.setItem('pwaInstalled', 'yes')
|
||||
pwaInstalled = true
|
||||
}
|
||||
if (pwaInstalled) {
|
||||
document.getElementById('installPWA').style.display = 'none'
|
||||
} else {
|
||||
document.getElementById('installPWA').style.display = 'block'
|
||||
}
|
||||
let deferredPrompt = null
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
deferredPrompt = e
|
||||
})
|
||||
async function installPWA() {
|
||||
if (deferredPrompt) {
|
||||
deferredPrompt.prompt()
|
||||
deferredPrompt.userChoice.then(({
|
||||
outcome
|
||||
}) => {
|
||||
if (outcome === 'accepted') {
|
||||
console.log('Your PWA has been installed')
|
||||
} else {
|
||||
console.log('User chose to not install your PWA')
|
||||
}
|
||||
deferredPrompt = null
|
||||
})
|
||||
}
|
||||
}
|
||||
window.addEventListener('appinstalled', (evt) => {
|
||||
localStorage.setItem('pwaInstalled', 'yes')
|
||||
pwaInstalled = true
|
||||
document.getElementById('installPWA').style.display = 'none'
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
12
jsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./*"],
|
||||
"@/*": ["./*"],
|
||||
"~~/*": ["./*"],
|
||||
"@@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", ".nuxt", "dist"]
|
||||
}
|
||||
7
layouts/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# LAYOUTS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your Application Layouts.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
|
||||
279
layouts/default.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<header class="header">
|
||||
<div>
|
||||
<div class="slide-in">
|
||||
<nuxt-link to="/">
|
||||
<h1 class="logo">Postwoman</h1>
|
||||
</nuxt-link>
|
||||
<h3 class="tagline">API request builder</h3>
|
||||
</div>
|
||||
<a href="https://github.com/liyasthomas/postwoman" target="_blank" rel="noopener">
|
||||
<button class="icon">
|
||||
<img id="imgGitHub" src="~static/icons/github.svg" alt="GitHub" :style="logoStyle()" />
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<div class="columns">
|
||||
<aside class="nav-first">
|
||||
<nav class="primary-nav">
|
||||
<!--
|
||||
We're using manual checks for linkActive because the query string
|
||||
seems to mess up the nuxt-link active class.
|
||||
-->
|
||||
<nuxt-link to="/" :class="linkActive('/')" v-tooltip.right="'Home'" aria-label="Home">
|
||||
<logo alt style="height: 24px;"></logo>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
to="/websocket"
|
||||
:class="linkActive('/websocket')"
|
||||
v-tooltip.right="'WebSocket'"
|
||||
>
|
||||
<i class="material-icons">cloud</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
to="/settings"
|
||||
:class="linkActive('/settings')"
|
||||
v-tooltip.right="'Settings'"
|
||||
aria-label="Settings"
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
</nuxt-link>
|
||||
</nav>
|
||||
<div v-if="['/'].includes($route.path)">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#request" v-tooltip.right="'Request'">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#options" v-tooltip.right="'Options'">
|
||||
<i class="material-icons">toc</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#response" v-tooltip.right="'Response'">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#collections" v-tooltip.right="'Collections'">
|
||||
<i class="material-icons">folder_special</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#history" v-tooltip.right="'History'">
|
||||
<i class="material-icons">watch_later</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="['/websocket'].includes($route.path)">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#request" v-tooltip.right="'Request'">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#response" v-tooltip.right="'Response'">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="['/settings'].includes($route.path)">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#theme" v-tooltip.right="'Theme'">
|
||||
<i class="material-icons">brush</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#proxy" v-tooltip.right="'Proxy'">
|
||||
<i class="material-icons">public</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
<nuxt id="main" class="main" />
|
||||
<aside class="nav-second"></aside>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<!-- Top section of footer: GitHub/install links -->
|
||||
<div class="flex-wrap">
|
||||
<button class="icon" id="installPWA" @click.prevent="showInstallPrompt()">
|
||||
<i class="material-icons">add_to_home_screen</i>
|
||||
<span>Install PWA</span>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Postwoman • API request builder - Helps you create your requests faster, saving you precious time on your development&url=https://postwoman.io&hashtags=postwoman&via=liyasthomas');"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Tweet</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Bottom section of footer: version/author information -->
|
||||
<p class="align-center">
|
||||
<span v-if="version.name">
|
||||
<a
|
||||
v-bind:href="'https://github.com/liyasthomas/postwoman/releases/tag/' + version.name"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{version.name}}</a>
|
||||
<span v-if="version.hash">
|
||||
-
|
||||
<a
|
||||
v-bind:href="'https://github.com/liyasthomas/postwoman/commit/' + version.hash"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{version.hash}}</a>
|
||||
</span>
|
||||
<span v-if="version.variant">({{version.variant}})</span>
|
||||
•
|
||||
</span> by
|
||||
<a href="https://liyasthomas.web.app" target="_blank" rel="noopener">Liyas Thomas 🦄</a> •
|
||||
<a href="https://postwoman.launchaco.com" target="_blank" rel="noopener">Subscribe</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import intializePwa from "../assets/js/pwa";
|
||||
import logo from "../components/logo";
|
||||
import * as version from "../.postwoman/version.json";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
logo
|
||||
},
|
||||
|
||||
methods: {
|
||||
linkActive(path) {
|
||||
return {
|
||||
"nuxt-link-exact-active": this.$route.path === path,
|
||||
"nuxt-link-active": this.$route.path === path
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// Once the PWA code is initialized, this holds a method
|
||||
// that can be called to show the user the installation
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
logoStyle() {
|
||||
return (
|
||||
this.$store.state.postwoman.settings.THEME_CLASS || ""
|
||||
).includes("light")
|
||||
? " filter: invert(100%); -webkit-filter: invert(100%);"
|
||||
: "";
|
||||
},
|
||||
|
||||
version: {}
|
||||
};
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
// Set version data
|
||||
this.version = version.default;
|
||||
|
||||
// Load theme settings
|
||||
(() => {
|
||||
// Apply theme from settings.
|
||||
document.documentElement.className =
|
||||
this.$store.state.postwoman.settings.THEME_CLASS || "";
|
||||
// Load theme color data from settings, or use default color.
|
||||
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b";
|
||||
let vibrant = this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT;
|
||||
if (vibrant == null) vibrant = true;
|
||||
document.documentElement.style.setProperty("--ac-color", color);
|
||||
document.documentElement.style.setProperty(
|
||||
"--act-color",
|
||||
vibrant ? "rgb(37, 38, 40)" : "#ffffff"
|
||||
);
|
||||
})();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (process.client) {
|
||||
document.body.classList.add("afterLoad");
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector("meta[name=theme-color]")
|
||||
.setAttribute(
|
||||
"content",
|
||||
this.$store.state.postwoman.settings.THEME_TAB_COLOR || "#252628"
|
||||
);
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
// etc.
|
||||
(async () => {
|
||||
this.showInstallPrompt = await intializePwa();
|
||||
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes";
|
||||
if (!cookiesAllowed) {
|
||||
this.$toast.show("We use cookies", {
|
||||
icon: "info",
|
||||
duration: 5000,
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: "Dismiss",
|
||||
onClick: (e, toastObject) => {
|
||||
localStorage.setItem("cookiesAllowed", "yes");
|
||||
toastObject.goAway(0);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
window.addEventListener("scroll", event => {
|
||||
let mainNavLinks = document.querySelectorAll("nav ul li a");
|
||||
let fromTop = window.scrollY;
|
||||
mainNavLinks.forEach(link => {
|
||||
let section = document.querySelector(link.hash);
|
||||
|
||||
if (
|
||||
section.offsetTop <= fromTop &&
|
||||
section.offsetTop + section.offsetHeight > fromTop
|
||||
) {
|
||||
link.classList.add("current");
|
||||
} else {
|
||||
link.classList.remove("current");
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route() {
|
||||
this.$toast.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
50
layouts/error.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="page page-error">
|
||||
<img src="~static/icons/error.svg" alt="Error" class="error_banner" />
|
||||
<h2>{{ error.statusCode }}</h2>
|
||||
<h3>{{ error.message }}</h3>
|
||||
<p>
|
||||
<nuxt-link to="/">
|
||||
<button>Go Home</button>
|
||||
</nuxt-link>
|
||||
</p>
|
||||
<p>
|
||||
<a href @click.prevent="reloadApplication">Reload</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
// Center the error page in the viewport.
|
||||
.page-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error_banner {
|
||||
width: 256px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["error"],
|
||||
|
||||
methods: {
|
||||
reloadApplication() {
|
||||
this.$router.push("/", () => window.location.reload());
|
||||
}
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
bodyAttrs: {
|
||||
class: "sticky-footer"
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "Postwoman",
|
||||
"short_name": "Postwoman",
|
||||
"description": "API request builder",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "#121212",
|
||||
"background_color": "#121212"
|
||||
}
|
||||
7
middleware/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# MIDDLEWARE
|
||||
<br/>
|
||||
parsedefaulturl.js - parse default url for appropriate path
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
|
||||
8
middleware/parsedefaulturl.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function ({
|
||||
route,
|
||||
redirect
|
||||
}) {
|
||||
if (route.fullPath !== '/') {
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
299
nuxt.config.js
Normal file
@@ -0,0 +1,299 @@
|
||||
// Some helpful application constants.
|
||||
// TODO: Use these when rendering the pages (rather than just for head/meta tags...)
|
||||
export const meta = {
|
||||
name: "Postwoman",
|
||||
shortDescription: "API request builder",
|
||||
description: "The Postwoman API request builder helps you create your requests faster, saving you precious time on your development."
|
||||
};
|
||||
// Sets the base path for the router.
|
||||
// Important for deploying to GitHub pages.
|
||||
// -- Travis includes the author in the repo slug,
|
||||
// so if there's a /, we need to get everything after it.
|
||||
let repoName = (process.env.TRAVIS_REPO_SLUG || '').split('/').pop();
|
||||
export const routerBase = process.env.DEPLOY_ENV === 'GH_PAGES' ? {
|
||||
router: {
|
||||
base: `/${repoName}/`
|
||||
}
|
||||
} : {
|
||||
router: {
|
||||
base: '/'
|
||||
}
|
||||
};
|
||||
export default {
|
||||
mode: 'spa',
|
||||
/*
|
||||
** Headers of the page
|
||||
*/
|
||||
server: {
|
||||
host: '0.0.0.0', // default: localhost
|
||||
},
|
||||
head: {
|
||||
title: `${meta.name} \u2022 ${meta.shortDescription}`,
|
||||
meta: [{
|
||||
charset: 'utf-8'
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no, minimal-ui'
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: meta.description || ''
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: 'postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket'
|
||||
},
|
||||
{
|
||||
name: 'X-UA-Compatible',
|
||||
content: "IE=edge, chrome=1"
|
||||
},
|
||||
{
|
||||
itemprop: "name",
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
itemprop: "description",
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
itemprop: "image",
|
||||
content: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
},
|
||||
// Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt)
|
||||
{
|
||||
name: 'application-name',
|
||||
content: meta.name
|
||||
},
|
||||
// Add to homescreen for Safari on iOS
|
||||
{
|
||||
name: 'apple-mobile-web-app-capable',
|
||||
content: 'yes'
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-status-bar-style',
|
||||
content: 'black-translucent'
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: meta.name
|
||||
},
|
||||
// Windows phone tile icon
|
||||
{
|
||||
name: 'msapplication-TileImage',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
name: 'msapplication-TileColor',
|
||||
content: '#252628'
|
||||
},
|
||||
{
|
||||
name: 'msapplication-tap-highlight',
|
||||
content: 'no'
|
||||
},
|
||||
// OpenGraph
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: meta.name
|
||||
},
|
||||
{
|
||||
property: 'og:url',
|
||||
content: 'https://postwoman.io'
|
||||
},
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'website'
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
// Twitter
|
||||
{
|
||||
name: 'twitter:card',
|
||||
content: "summary"
|
||||
},
|
||||
{
|
||||
name: 'twitter:site',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:creator',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:url',
|
||||
content: "https://postwoman.io"
|
||||
},
|
||||
{
|
||||
name: 'twitter:title',
|
||||
content: meta.name
|
||||
},
|
||||
{
|
||||
name: 'twitter:description',
|
||||
content: meta.shortDescription
|
||||
},
|
||||
{
|
||||
name: 'twitter:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
],
|
||||
link: [{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: `${routerBase.router.base}favicon.ico`
|
||||
},
|
||||
// Home-screen icons (iOS)
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
href: `${routerBase.router.base}icons/icon-48x48.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '72x72',
|
||||
href: `${routerBase.router.base}icons/icon-72x72.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '96x96',
|
||||
href: `${routerBase.router.base}icons/icon-96x96.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '144x144',
|
||||
href: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '192x192',
|
||||
href: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
},
|
||||
]
|
||||
},
|
||||
/*
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: {
|
||||
color: 'var(--ac-color)'
|
||||
},
|
||||
/*
|
||||
** Customize the loading indicator
|
||||
*/
|
||||
loadingIndicator: {
|
||||
name: 'pulse',
|
||||
color: 'var(--ac-color)',
|
||||
background: 'var(--bg-color)'
|
||||
},
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
css: [
|
||||
'@/assets/css/themes.scss',
|
||||
'@/assets/css/fonts.scss',
|
||||
'@/assets/css/styles.scss'
|
||||
],
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [{
|
||||
src: '~/plugins/vuex-persist'
|
||||
},
|
||||
{
|
||||
src: '~/plugins/v-tooltip'
|
||||
}
|
||||
],
|
||||
/*
|
||||
** Nuxt.js dev-modules
|
||||
*/
|
||||
buildModules: [],
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
modules: [
|
||||
// See https://goo.gl/OOhYW5
|
||||
['@nuxtjs/pwa'],
|
||||
['@nuxtjs/axios'],
|
||||
['@nuxtjs/toast'],
|
||||
['@nuxtjs/google-analytics'],
|
||||
['@nuxtjs/sitemap'],
|
||||
['@nuxtjs/google-tag-manager', {
|
||||
id: process.env.GTM_ID || 'GTM-MXWD8NQ'
|
||||
}],
|
||||
['@nuxtjs/robots']
|
||||
],
|
||||
pwa: {
|
||||
manifest: {
|
||||
name: meta.name,
|
||||
short_name: meta.name,
|
||||
|
||||
display: "standalone",
|
||||
|
||||
theme_color: "#252628",
|
||||
background_color: "#252628",
|
||||
start_url: `${routerBase.router.base}`
|
||||
},
|
||||
|
||||
meta: {
|
||||
description: meta.shortDescription,
|
||||
theme_color: "#252628",
|
||||
},
|
||||
|
||||
icons: ((sizes) => {
|
||||
let icons = [];
|
||||
for (let size of sizes) {
|
||||
icons.push({
|
||||
"src": `${routerBase.router.base}icons/icon-${size}x${size}.png`,
|
||||
"type": "image/png",
|
||||
"sizes": `${size}x${size}`
|
||||
});
|
||||
}
|
||||
return icons;
|
||||
})([48, 72, 96, 144, 192, 512])
|
||||
},
|
||||
toast: {
|
||||
position: 'bottom-center',
|
||||
duration: 3000,
|
||||
theme: 'bubble',
|
||||
keepOnHover: true
|
||||
},
|
||||
googleAnalytics: {
|
||||
id: process.env.GA_ID || 'UA-61422507-2'
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://postwoman.io'
|
||||
},
|
||||
robots: {
|
||||
UserAgent: '*',
|
||||
Disallow: '',
|
||||
Allow: '/',
|
||||
Sitemap: 'https://postwoman.io/sitemap.xml'
|
||||
},
|
||||
/*
|
||||
** Build configuration
|
||||
*/
|
||||
build: {
|
||||
/*
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
extend(config, ctx) {}
|
||||
},
|
||||
/*
|
||||
** Generate configuration
|
||||
*/
|
||||
generate: {
|
||||
fallback: true
|
||||
},
|
||||
/*
|
||||
** Router configuration
|
||||
*/
|
||||
...routerBase
|
||||
}
|
||||
12364
package-lock.json
generated
Normal file
54
package.json
@@ -1,16 +1,42 @@
|
||||
{
|
||||
"name": "Banner",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/liyasthomas/banner.git"
|
||||
},
|
||||
"description": "A simple and clean banner generator",
|
||||
"author": "liyasthomas",
|
||||
"version": "0.1.9",
|
||||
"devDependencies": {
|
||||
"jshint": "^2.10.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jshint travis.js"
|
||||
}
|
||||
"name": "postwoman",
|
||||
"version": "1.0.0",
|
||||
"description": "A free, fast, and beautiful API request builder",
|
||||
"author": "liyasthomas",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "node build.js --dev",
|
||||
"dev": "nuxt",
|
||||
"prebuild": "node build.js",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"pregenerate": "node build.js",
|
||||
"generate": "nuxt generate",
|
||||
"e2e": "cypress run",
|
||||
"e2e:open": "cypress open",
|
||||
"dev:e2e": "start-server-and-test dev http://localhost:3000 e2e:open",
|
||||
"test": "start-server-and-test dev http://localhost:3000 e2e"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.8.0",
|
||||
"@nuxtjs/google-analytics": "^2.2.1",
|
||||
"@nuxtjs/google-tag-manager": "^2.3.1",
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
||||
"@nuxtjs/robots": "^2.4.2",
|
||||
"@nuxtjs/sitemap": "^2.0.0",
|
||||
"@nuxtjs/toast": "^3.3.0",
|
||||
"highlight.js": "^9.16.2",
|
||||
"nuxt": "^2.10.2",
|
||||
"v-tooltip": "^2.0.2",
|
||||
"vue-virtual-scroll-list": "^1.4.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^2.1.1",
|
||||
"yargs-parser": "^16.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^3.6.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"start-server-and-test": "^1.10.6"
|
||||
}
|
||||
}
|
||||
|
||||
6
pages/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# PAGES
|
||||
|
||||
This directory contains your Application Views and Routes.
|
||||
The framework reads all the `*.vue` files inside this directory and creates the router of your application.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
|
||||
1769
pages/index.vue
Normal file
279
pages/settings.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="cyan" label="Theme" ref="theme">
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Background</h3>
|
||||
<div class="backgrounds">
|
||||
<span
|
||||
:key="theme.class"
|
||||
@click="applyTheme(theme.class, theme.color)"
|
||||
v-for="theme in themes"
|
||||
>
|
||||
<swatch
|
||||
:active="settings.THEME_CLASS === theme.class"
|
||||
:class="{ vibrant: theme.vibrant }"
|
||||
:color="theme.color"
|
||||
:name="theme.name"
|
||||
></swatch>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Color</h3>
|
||||
<div class="colors">
|
||||
<span
|
||||
:key="entry.color"
|
||||
@click.prevent="setActiveColor(entry.color, entry.vibrant)"
|
||||
v-for="entry in colors"
|
||||
>
|
||||
<swatch
|
||||
:active="settings.THEME_COLOR === entry.color.toUpperCase()"
|
||||
:class="{ vibrant: entry.vibrant }"
|
||||
:color="entry.color"
|
||||
:name="entry.name"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Labels</h3>
|
||||
<span>
|
||||
<pw-toggle
|
||||
:on="settings.FRAME_COLORS_ENABLED"
|
||||
@change="toggleSetting('FRAME_COLORS_ENABLED')"
|
||||
>Multi-color {{ settings.FRAME_COLORS_ENABLED ? "Enabled" : "Disabled" }}</pw-toggle>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<br />
|
||||
|
||||
<pw-section class="blue" label="Proxy" ref="proxy">
|
||||
<ul>
|
||||
<li>
|
||||
<span>
|
||||
<pw-toggle
|
||||
:on="settings.PROXY_ENABLED"
|
||||
@change="toggleSetting('PROXY_ENABLED')"
|
||||
>Proxy {{ settings.PROXY_ENABLED ? "enabled" : "disabled" }}</pw-toggle>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="info">
|
||||
<li>
|
||||
<p>
|
||||
Postwoman's Proxy is hosted by ApolloTV.
|
||||
<br />Read the ApolloTV privacy policy
|
||||
<a
|
||||
href="https://apollotv.xyz/legal"
|
||||
target="_blank"
|
||||
>here</a>.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<!--
|
||||
PROXY SETTINGS URL AND KEY
|
||||
--------------
|
||||
This feature is currently not finished.
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input id="url" type="url" v-model="settings.PROXY_URL" :disabled="!settings.PROXY_ENABLED">
|
||||
</li>
|
||||
<li>
|
||||
<label for="key">Key</label>
|
||||
<input id="key" type="password" v-model="settings.PROXY_KEY" :disabled="!settings.PROXY_ENABLED" @change="applySetting('PROXY_KEY', $event)">
|
||||
</li>
|
||||
</ul>
|
||||
-->
|
||||
</pw-section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.info {
|
||||
margin-left: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
import swatch from "../components/settings/swatch";
|
||||
import toggle from "../components/toggle";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section,
|
||||
"pw-toggle": toggle,
|
||||
swatch: swatch
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// NOTE:: You need to first set the CSS for your theme in /assets/css/themes.scss
|
||||
// You should copy the existing light theme as a template and then just
|
||||
// set the relevant values.
|
||||
themes: [
|
||||
{
|
||||
color: "#252628",
|
||||
name: "Kinda Dark",
|
||||
class: ""
|
||||
},
|
||||
{
|
||||
color: "#ffffff",
|
||||
name: "Clearly White",
|
||||
vibrant: true,
|
||||
class: "light"
|
||||
},
|
||||
{
|
||||
color: "#000000",
|
||||
name: "Just Black",
|
||||
class: "black"
|
||||
},
|
||||
{
|
||||
color: "var(--bg-color)",
|
||||
name: "Auto (system)",
|
||||
vibrant: window.matchMedia("(prefers-color-scheme: light)").matches,
|
||||
class: "auto"
|
||||
}
|
||||
],
|
||||
// You can define a new color here! It will simply store the color value.
|
||||
colors: [
|
||||
// If the color is vibrant, black is used as the active foreground color.
|
||||
{
|
||||
color: "#50fa7b",
|
||||
name: "Green",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#f1fa8c",
|
||||
name: "Yellow",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ff79c6",
|
||||
name: "Pink",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ff5555",
|
||||
name: "Red",
|
||||
vibrant: false
|
||||
},
|
||||
{
|
||||
color: "#bd93f9",
|
||||
name: "Purple",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ffb86c",
|
||||
name: "Orange",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#8be9fd",
|
||||
name: "Cyan",
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#57b5f9",
|
||||
name: "Blue",
|
||||
vibrant: false
|
||||
}
|
||||
],
|
||||
|
||||
settings: {
|
||||
THEME_CLASS: this.$store.state.postwoman.settings.THEME_CLASS || "",
|
||||
THEME_COLOR: "",
|
||||
THEME_TAB_COLOR: "",
|
||||
THEME_COLOR_VIBRANT: true,
|
||||
|
||||
FRAME_COLORS_ENABLED:
|
||||
this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false,
|
||||
PROXY_ENABLED:
|
||||
this.$store.state.postwoman.settings.PROXY_ENABLED || false,
|
||||
PROXY_URL: this.$store.state.postwoman.settings.PROXY_URL || "",
|
||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ""
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
proxySettings: {
|
||||
deep: true,
|
||||
handler(value) {
|
||||
this.applySetting("PROXY_URL", value.url);
|
||||
this.applySetting("PROXY_KEY", value.key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
applyTheme(name, color) {
|
||||
this.applySetting("THEME_CLASS", name);
|
||||
document
|
||||
.querySelector("meta[name=theme-color]")
|
||||
.setAttribute("content", color);
|
||||
this.applySetting("THEME_TAB_COLOR", color);
|
||||
document.documentElement.className = name;
|
||||
let imgGitHub = document.getElementById("imgGitHub");
|
||||
imgGitHub.style["filter"] = "";
|
||||
imgGitHub.style["webkit-filter"] = "invert(100%)";
|
||||
if (name.includes("light")) {
|
||||
imgGitHub.style["filter"] = "invert(100%)";
|
||||
imgGitHub.style["webkit-filter"] = "invert(100%)";
|
||||
}
|
||||
},
|
||||
setActiveColor(color, vibrant) {
|
||||
// By default, the color is vibrant.
|
||||
if (vibrant == null) vibrant = true;
|
||||
document.documentElement.style.setProperty("--ac-color", color);
|
||||
document.documentElement.style.setProperty(
|
||||
"--act-color",
|
||||
vibrant ? "rgb(37, 38, 40)" : "#f8f8f2"
|
||||
);
|
||||
this.applySetting("THEME_COLOR", color.toUpperCase());
|
||||
this.applySetting("THEME_COLOR_VIBRANT", vibrant);
|
||||
},
|
||||
getActiveColor() {
|
||||
// This strips extra spaces and # signs from the strings.
|
||||
const strip = str => str.replace(/#/g, "").replace(/ /g, "");
|
||||
return `#${strip(
|
||||
window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--ac-color")
|
||||
).toUpperCase()}`;
|
||||
},
|
||||
applySetting(key, value) {
|
||||
this.settings[key] = value;
|
||||
this.$store.commit("postwoman/applySetting", [key, value]);
|
||||
},
|
||||
toggleSetting(key) {
|
||||
this.settings[key] = !this.settings[key];
|
||||
this.$store.commit("postwoman/applySetting", [key, this.settings[key]]);
|
||||
this.$router.replace("/settings", {
|
||||
force: true
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.settings.THEME_COLOR = this.getActiveColor();
|
||||
},
|
||||
|
||||
computed: {
|
||||
proxySettings() {
|
||||
return {
|
||||
url: this.settings.PROXY_URL,
|
||||
key: this.settings.PROXY_KEY
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
244
pages/websocket.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="blue" label="Request" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input
|
||||
id="url"
|
||||
type="url"
|
||||
:class="{ error: !urlValid }"
|
||||
v-model="url"
|
||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="connect" class="hide-on-small-screen"> </label>
|
||||
<button :disabled="!urlValid" id="connect" name="connect" @click="toggleConnection">
|
||||
{{ toggleConnectionVerb }}
|
||||
<span>
|
||||
<i class="material-icons" v-if="!connectionState">sync</i>
|
||||
<i class="material-icons" v-if="connectionState">sync_disabled</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<br />
|
||||
|
||||
<pw-section class="purple" label="Communication" id="response" ref="response">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="log">Log</label>
|
||||
<div id="log" name="log" class="log">
|
||||
<span v-if="communication.log">
|
||||
<span
|
||||
v-for="(logEntry, index) in communication.log"
|
||||
:style="{ color: logEntry.color }"
|
||||
:key="index"
|
||||
>@ {{ logEntry.ts }} {{ getSourcePrefix(logEntry.source) }} {{ logEntry.payload }}</span>
|
||||
</span>
|
||||
<span v-else>(waiting for connection)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="message">Message</label>
|
||||
<input
|
||||
id="message"
|
||||
name="message"
|
||||
type="text"
|
||||
v-model="communication.input"
|
||||
:readonly="!connectionState"
|
||||
@keyup.enter="connectionState ? sendMessage() : null"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="send" class="hide-on-small-screen"> </label>
|
||||
<button id="send" name="send" :disabled="!connectionState" @click="sendMessage">
|
||||
Send
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
div.log {
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
width: calc(100% - 8px);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
height: 256px;
|
||||
overflow: auto;
|
||||
|
||||
&,
|
||||
span {
|
||||
font-size: 18px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
connectionState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
socket: null,
|
||||
communication: {
|
||||
log: null,
|
||||
input: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toggleConnectionVerb() {
|
||||
return !this.connectionState ? "Connect" : "Disconnect";
|
||||
},
|
||||
urlValid() {
|
||||
const pattern = new RegExp(
|
||||
"^(wss?:\\/\\/)?" +
|
||||
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" +
|
||||
"((\\d{1,3}\\.){3}\\d{1,3}))" +
|
||||
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" +
|
||||
"(\\?[;&a-z\\d%_.~+=-]*)?" +
|
||||
"(\\#[-a-z\\d_]*)?$",
|
||||
"i"
|
||||
);
|
||||
return pattern.test(this.url);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleConnection() {
|
||||
// If it is connecting:
|
||||
if (!this.connectionState) return this.connect();
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.disconnect();
|
||||
},
|
||||
connect() {
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connecting to ${this.url}...`,
|
||||
source: "info",
|
||||
color: "var(--ac-color)"
|
||||
}
|
||||
];
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
this.socket.onopen = event => {
|
||||
this.connectionState = true;
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connected to ${this.url}.`,
|
||||
source: "info",
|
||||
color: "var(--ac-color)",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
}
|
||||
];
|
||||
this.$toast.success("Connected", {
|
||||
icon: "sync"
|
||||
});
|
||||
};
|
||||
this.socket.onerror = event => {
|
||||
this.handleError();
|
||||
};
|
||||
this.socket.onclose = event => {
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: `Disconnected from ${this.url}.`,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.$toast.error("Disconnected", {
|
||||
icon: "sync_disabled"
|
||||
});
|
||||
};
|
||||
this.socket.onmessage = event => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
};
|
||||
} catch (ex) {
|
||||
this.handleError(ex);
|
||||
this.$toast.error("Something went wrong!", {
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
},
|
||||
disconnect() {
|
||||
if (this.socket != null) this.socket.close();
|
||||
},
|
||||
handleError(error) {
|
||||
this.disconnect();
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: `An error has occurred.`,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
if (error != null)
|
||||
this.communication.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
},
|
||||
sendMessage() {
|
||||
const message = this.communication.input;
|
||||
this.socket.send(message);
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: "client",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.communication.input = "";
|
||||
},
|
||||
collapse({ target }) {
|
||||
const el = target.parentNode.className;
|
||||
document.getElementsByClassName(el)[0].classList.toggle("hidden");
|
||||
},
|
||||
getSourcePrefix(source) {
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
info: "\tℹ️ [INFO]:\t",
|
||||
// Source used for client to server messages.
|
||||
client: "\t👽 [SENT]:\t",
|
||||
// Source used for server to client messages.
|
||||
server: "\t📥 [RECEIVED]:\t"
|
||||
};
|
||||
if (Object.keys(sourceEmojis).includes(source))
|
||||
return sourceEmojis[source];
|
||||
return "";
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
this.$nextTick(function() {
|
||||
var divLog = document.getElementById("log");
|
||||
divLog.scrollBy(0, divLog.scrollHeight + 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
7
plugins/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# PLUGINS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
|
||||
4
plugins/v-tooltip.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import Vue from 'vue';
|
||||
import VTooltip from 'v-tooltip';
|
||||
|
||||
Vue.use(VTooltip);
|
||||
7
plugins/vuex-persist.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import VuexPersistence from "vuex-persist";
|
||||
|
||||
export default ({
|
||||
store
|
||||
}) => {
|
||||
new VuexPersistence().plugin(store);
|
||||
}
|
||||
123
script.js
@@ -1,123 +0,0 @@
|
||||
const parseHeaders = xhr => {
|
||||
const headers = xhr.getAllResponseHeaders().trim().split(/[\r\n]+/)
|
||||
const headerMap = {}
|
||||
headers.forEach(line => {
|
||||
const parts = line.split(': ')
|
||||
const header = parts.shift().toLowerCase()
|
||||
const value = parts.join(': ')
|
||||
headerMap[header] = value
|
||||
})
|
||||
return headerMap
|
||||
}
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
method: 'GET',
|
||||
url: 'https://yesno.wtf',
|
||||
auth: 'None',
|
||||
path: '/api',
|
||||
httpUser: '',
|
||||
httpPassword: '',
|
||||
params: [],
|
||||
bodyParams: [],
|
||||
contentType: 'application/json',
|
||||
response: {
|
||||
status: '',
|
||||
headers: '',
|
||||
body: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rawRequestBody() {
|
||||
const {
|
||||
bodyParams
|
||||
} = this
|
||||
if (this.contentType === 'application/json') {
|
||||
try {
|
||||
const obj = JSON.parse(`{${bodyParams.filter(({ key }) => !!key).map(({ key, value }) => `
|
||||
"${key}": "${value}"
|
||||
`).join()}}`)
|
||||
return JSON.stringify(obj)
|
||||
} catch (ex) {
|
||||
return 'invalid'
|
||||
}
|
||||
} else {
|
||||
return bodyParams
|
||||
.filter(({
|
||||
key
|
||||
}) => !!key)
|
||||
.map(({
|
||||
key,
|
||||
value
|
||||
}) => `${key}=${encodeURIComponent(value)}`).join('&')
|
||||
}
|
||||
},
|
||||
queryString() {
|
||||
const result = this.params
|
||||
.filter(({
|
||||
key
|
||||
}) => !!key)
|
||||
.map(({
|
||||
key,
|
||||
value
|
||||
}) => `${key}=${encodeURIComponent(value)}`).join('&')
|
||||
return result == '' ? '' : `?${result}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
collapse({
|
||||
target
|
||||
}) {
|
||||
const el = target.parentNode.className
|
||||
document.getElementsByClassName(el)[0].classList.toggle('hidden')
|
||||
},
|
||||
sendRequest() {
|
||||
this.$refs.response.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
})
|
||||
this.response.status = 'Fetching...'
|
||||
this.response.body = 'Loading...'
|
||||
const xhr = new XMLHttpRequest()
|
||||
const user = this.auth === 'Basic' ? this.httpUser : null
|
||||
const pswd = this.auth === 'Basic' ? this.httpPassword : null
|
||||
xhr.open(this.method, this.url + this.path + this.queryString, true, user, pswd)
|
||||
if (this.method === 'POST' || this.method === 'PUT') {
|
||||
const requestBody = this.rawRequestBody
|
||||
xhr.setRequestHeader('Content-Length', requestBody.length)
|
||||
xhr.setRequestHeader('Content-Type', `${this.contentType}; charset=utf-8`)
|
||||
xhr.send(requestBody)
|
||||
} else {
|
||||
xhr.send()
|
||||
}
|
||||
xhr.onload = e => {
|
||||
this.response.status = xhr.status
|
||||
const headers = this.response.headers = parseHeaders(xhr)
|
||||
if ((headers['content-type'] || '').startsWith('application/json')) {
|
||||
this.response.body = JSON.stringify(JSON.parse(xhr.responseText), null, 2)
|
||||
} else {
|
||||
this.response.body = xhr.responseText
|
||||
}
|
||||
}
|
||||
},
|
||||
addRequestParam() {
|
||||
this.params.push({
|
||||
key: '',
|
||||
value: ''
|
||||
})
|
||||
return false
|
||||
},
|
||||
removeRequestParam(index) {
|
||||
this.params.splice(index, 1)
|
||||
},
|
||||
addRequestBodyParam() {
|
||||
this.bodyParams.push({
|
||||
key: '',
|
||||
value: ''
|
||||
})
|
||||
return false
|
||||
},
|
||||
removeRequestBodyParam(index) {
|
||||
this.bodyParams.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
16
static/.htaccess
Normal file
@@ -0,0 +1,16 @@
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
|
||||
# Images
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/gif "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
|
||||
# CSS, JavaScript
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType text/javascript "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
</IfModule>
|
||||
1
static/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
postwoman.io
|
||||
11
static/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# STATIC
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your static files.
|
||||
Each file inside this directory is mapped to `/`.
|
||||
Thus you'd want to delete this README.md before deploying to production.
|
||||
|
||||
Example: `/static/robots.txt` is mapped as `/robots.txt`.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
|
||||
BIN
static/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
static/icon.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
1
static/icons/error.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
static/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |