mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
Compare commits
892 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ff4bf9293c | ||
![]() |
0c423c999c | ||
![]() |
e3a5a52506 | ||
![]() |
9a0e259a9b | ||
![]() |
5305f01011 | ||
![]() |
90b88959d0 | ||
![]() |
7cc4cce1a3 | ||
![]() |
03735dc09c | ||
![]() |
1c095b13e9 | ||
![]() |
faff9efaca | ||
![]() |
7a2767a763 | ||
![]() |
c47800f4d8 | ||
![]() |
e584906a65 | ||
![]() |
3070627c83 | ||
![]() |
3bd809818f | ||
![]() |
61df63c96e | ||
![]() |
7eb6d38cfc | ||
![]() |
638c2c3c23 | ||
![]() |
5c52df2eee | ||
![]() |
7cb337c158 | ||
![]() |
e07c9b21ec | ||
![]() |
203bd40932 | ||
![]() |
3688ba1cc6 | ||
![]() |
78a40daf46 | ||
![]() |
d7f19473a4 | ||
![]() |
dc5f90ad61 | ||
![]() |
2b4f750392 | ||
![]() |
c440a7cc88 | ||
![]() |
6dfaaf229b | ||
![]() |
5ff33b7ac3 | ||
![]() |
efb2552997 | ||
![]() |
5d3cbaca19 | ||
![]() |
96b1402a65 | ||
![]() |
0251de11ec | ||
![]() |
1862fb69fd | ||
![]() |
f882b86889 | ||
![]() |
7f6a0b27c3 | ||
![]() |
0cb9706393 | ||
![]() |
4c01db9a67 | ||
![]() |
1304ef90e8 | ||
![]() |
8c4d38e30e | ||
![]() |
30eff267ea | ||
![]() |
580058056f | ||
![]() |
bfedfc027c | ||
![]() |
bf1565398b | ||
![]() |
8b1f055cd1 | ||
![]() |
ca0e7e35ae | ||
![]() |
e15416ff2d | ||
![]() |
0e7121b8f4 | ||
![]() |
2183e6acdf | ||
![]() |
48b5a0e765 | ||
![]() |
1ddf797fff | ||
![]() |
2afde8db4a | ||
![]() |
c6b9fa8d13 | ||
![]() |
c9283d8fa6 | ||
![]() |
58f9135f6b | ||
![]() |
b79e5bbad7 | ||
![]() |
b14d5da558 | ||
![]() |
9433e5ca06 | ||
![]() |
73b661a6a5 | ||
![]() |
9e616ea3a9 | ||
![]() |
1bdab7394a | ||
![]() |
d732a9e48b | ||
![]() |
c274977eb6 | ||
![]() |
81a29cdd60 | ||
![]() |
713380ccd2 | ||
![]() |
6f6d8b533d | ||
![]() |
0665787e2b | ||
![]() |
2ee0bed0c1 | ||
![]() |
9abebd7673 | ||
![]() |
8778ca4138 | ||
![]() |
eca0451c4e | ||
![]() |
9ceb86b208 | ||
![]() |
47c070932a | ||
![]() |
73d54b6271 | ||
![]() |
2d3993125d | ||
![]() |
ad8e67e3c3 | ||
![]() |
6d7e980b5f | ||
![]() |
e9439daa51 | ||
![]() |
94c6a97ae7 | ||
![]() |
f25747443e | ||
![]() |
680f871aa3 | ||
![]() |
b125d8d150 | ||
![]() |
7123207c66 | ||
![]() |
89b24d3862 | ||
![]() |
d5c8a02980 | ||
![]() |
451c33a69a | ||
![]() |
07b30ed602 | ||
![]() |
d06359f45c | ||
![]() |
cedf9b8809 | ||
![]() |
e840df43af | ||
![]() |
ef59ea185c | ||
![]() |
233d2612d1 | ||
![]() |
cfad98b0c6 | ||
![]() |
3dbdd4e43c | ||
![]() |
db99ad65fb | ||
![]() |
5efedd7308 | ||
![]() |
8f6322efc0 | ||
![]() |
7ec5805d8d | ||
![]() |
faff04ff63 | ||
![]() |
3749bf74cf | ||
![]() |
027edcb54e | ||
![]() |
cdfe87c2b6 | ||
![]() |
9ca6e59d32 | ||
![]() |
8b52cbb2cb | ||
![]() |
e4e703c957 | ||
![]() |
d6d50040dc | ||
![]() |
809eae4f98 | ||
![]() |
081931a6d4 | ||
![]() |
5687bddc90 | ||
![]() |
ac52811880 | ||
![]() |
19cb91ee4e | ||
![]() |
46d926e831 | ||
![]() |
9c9a5fd293 | ||
![]() |
87652467b5 | ||
![]() |
de46cccc1e | ||
![]() |
fa1e1578f7 | ||
![]() |
42720c57ea | ||
![]() |
8facbcc095 | ||
![]() |
a634b37b1a | ||
![]() |
525d834784 | ||
![]() |
2beb258b4c | ||
![]() |
39337338bd | ||
![]() |
c85cb4365d | ||
![]() |
210a5834c6 | ||
![]() |
55682b6f4b | ||
![]() |
7335f8c866 | ||
![]() |
904b2f8e7f | ||
![]() |
ced9cde41b | ||
![]() |
94ab05b032 | ||
![]() |
589a7784dc | ||
![]() |
440bbffb73 | ||
![]() |
e3eaf5e268 | ||
![]() |
a092f5a725 | ||
![]() |
8f79ff7bb0 | ||
![]() |
ebe6f0cf3b | ||
![]() |
9d94b433d0 | ||
![]() |
ff3c4f6e69 | ||
![]() |
f062fc330b | ||
![]() |
4f19440262 | ||
![]() |
e4bb977d5d | ||
![]() |
0e8b4549df | ||
![]() |
f6b65c8e7e | ||
![]() |
5ea98f48e3 | ||
![]() |
41c2f64ef2 | ||
![]() |
2ff35aa316 | ||
![]() |
c4a613a971 | ||
![]() |
2bbbe36e13 | ||
![]() |
532b59b098 | ||
![]() |
d53d157f19 | ||
![]() |
1051e477ba | ||
![]() |
2c3e7d55fd | ||
![]() |
ba7c3e8bda | ||
![]() |
ddaf523a98 | ||
![]() |
666569f1d7 | ||
![]() |
05f497778a | ||
![]() |
f85f55c5a1 | ||
![]() |
ce20e0787d | ||
![]() |
852b77f49a | ||
![]() |
6780cb4334 | ||
![]() |
77162fcb68 | ||
![]() |
d1f13c86c6 | ||
![]() |
9413142c80 | ||
![]() |
6d3ea2ecc5 | ||
![]() |
3c5de47c42 | ||
![]() |
396b367e4e | ||
![]() |
1969d48e0f | ||
![]() |
0441aa332d | ||
![]() |
009be7e534 | ||
![]() |
74d2b077cd | ||
![]() |
50af2d52ad | ||
![]() |
3b8eb16960 | ||
![]() |
395c2b8f98 | ||
![]() |
17af355ac1 | ||
![]() |
49973ac522 | ||
![]() |
90c7fb813e | ||
![]() |
34f53a20fb | ||
![]() |
0b158667a7 | ||
![]() |
edf6c5f38d | ||
![]() |
c97ac0002c | ||
![]() |
d7282be14b | ||
![]() |
f8f2fc502d | ||
![]() |
63b0043076 | ||
![]() |
89c5286a52 | ||
![]() |
31971c0c51 | ||
![]() |
7dedd0797c | ||
![]() |
e52e5821c1 | ||
![]() |
eafab334a9 | ||
![]() |
d1a59e5d6f | ||
![]() |
d178d4a095 | ||
![]() |
8cd8c4f6fb | ||
![]() |
e63504a8df | ||
![]() |
ae0ffadc29 | ||
![]() |
bdf4717d2d | ||
![]() |
084e2907da | ||
![]() |
0eec835c31 | ||
![]() |
8e2f12542d | ||
![]() |
5df08f91cb | ||
![]() |
b8450ceab9 | ||
![]() |
1be6c5ed62 | ||
![]() |
4db88079b5 | ||
![]() |
6cb7261ac2 | ||
![]() |
dac9e8d059 | ||
![]() |
6336cc66ae | ||
![]() |
8e4128b474 | ||
![]() |
cd45008433 | ||
![]() |
087704ec6b | ||
![]() |
e7afd231eb | ||
![]() |
da573640b3 | ||
![]() |
44fd7bee89 | ||
![]() |
5ba2d9b44f | ||
![]() |
5d5432a9b5 | ||
![]() |
70eb769209 | ||
![]() |
8c8b513716 | ||
![]() |
5bafcea9f3 | ||
![]() |
a362460b0f | ||
![]() |
617c7ac2f1 | ||
![]() |
fdedf8e59d | ||
![]() |
b1b0432983 | ||
![]() |
fe9ea62f61 | ||
![]() |
10b42525ad | ||
![]() |
fc10c250c9 | ||
![]() |
0f9ebfc454 | ||
![]() |
7925598cd8 | ||
![]() |
8b9f8915f2 | ||
![]() |
804bf04e50 | ||
![]() |
2168e3e2d7 | ||
![]() |
5ca97d88a3 | ||
![]() |
429b011955 | ||
![]() |
e9ed2e5a02 | ||
![]() |
331ea85c86 | ||
![]() |
183555d793 | ||
![]() |
655bec7b16 | ||
![]() |
4a84984699 | ||
![]() |
4dc672eeb4 | ||
![]() |
508d38cd1b | ||
![]() |
45dd008cb7 | ||
![]() |
1c25266935 | ||
![]() |
ae6d5cb6bf | ||
![]() |
5be57c3fb1 | ||
![]() |
4ded14186f | ||
![]() |
88cfae52b4 | ||
![]() |
8de6ce5ca2 | ||
![]() |
60d94180e2 | ||
![]() |
43eae41c35 | ||
![]() |
d141c6e403 | ||
![]() |
edccff60a7 | ||
![]() |
cb3f8600d9 | ||
![]() |
00f75aff9a | ||
![]() |
1bbc308ceb | ||
![]() |
6e0a6fe225 | ||
![]() |
a6b6b7f2ee | ||
![]() |
49b2937421 | ||
![]() |
be0b1cefb2 | ||
![]() |
0a91b8079b | ||
![]() |
c17a14b53a | ||
![]() |
e2a92bd61e | ||
![]() |
34d7f6fc70 | ||
![]() |
d8d037fc81 | ||
![]() |
7d12a82458 | ||
![]() |
227685cedf | ||
![]() |
1e1cad8bbd | ||
![]() |
4d0bcab3b8 | ||
![]() |
b3e5b21f86 | ||
![]() |
deeb87e31b | ||
![]() |
0770648219 | ||
![]() |
5ee816191a | ||
![]() |
ab5e38b367 | ||
![]() |
002680ede4 | ||
![]() |
e1de0c875e | ||
![]() |
d7dd65babf | ||
![]() |
fece267263 | ||
![]() |
bff7804b5b | ||
![]() |
1eed73af48 | ||
![]() |
afa7abc646 | ||
![]() |
8a7d1ce2fc | ||
![]() |
52590d96b9 | ||
![]() |
4f3c5f0f3d | ||
![]() |
57d1919c68 | ||
![]() |
1703e6003c | ||
![]() |
4a66bface6 | ||
![]() |
badb490d50 | ||
![]() |
807ec9ef25 | ||
![]() |
bafb25dcfa | ||
![]() |
cf5c00d82c | ||
![]() |
77c39a2035 | ||
![]() |
564c6f3100 | ||
![]() |
c254f4c699 | ||
![]() |
0875201558 | ||
![]() |
803e1d4938 | ||
![]() |
5701b8b581 | ||
![]() |
2777d87c4d | ||
![]() |
ca003747b7 | ||
![]() |
c95b99d376 | ||
![]() |
c686a15980 | ||
![]() |
04bbfdd2eb | ||
![]() |
5d755af301 | ||
![]() |
bac237fa89 | ||
![]() |
37da3cc8ea | ||
![]() |
be6dcf0f99 | ||
![]() |
ebe8345090 | ||
![]() |
4dc88e9ea8 | ||
![]() |
80c74d254b | ||
![]() |
97bbdd9b74 | ||
![]() |
622a252722 | ||
![]() |
37726f6e68 | ||
![]() |
84097d7e68 | ||
![]() |
69165f69f5 | ||
![]() |
05349a5c68 | ||
![]() |
fe57813721 | ||
![]() |
773d5cba25 | ||
![]() |
7e9ec5d93a | ||
![]() |
866e8594d6 | ||
![]() |
11bdf2211e | ||
![]() |
811a1b8963 | ||
![]() |
a607bdd004 | ||
![]() |
33b29f4ee6 | ||
![]() |
fee7473ccb | ||
![]() |
fd6752cbd6 | ||
![]() |
a89ca678bb | ||
![]() |
c82e0da589 | ||
![]() |
b78bbc322b | ||
![]() |
35c8a75a00 | ||
![]() |
bf68704063 | ||
![]() |
5cbd5e3943 | ||
![]() |
bd417b9f04 | ||
![]() |
f723c69d7c | ||
![]() |
a394a23aa1 | ||
![]() |
8161f14553 | ||
![]() |
87b2dbe03d | ||
![]() |
85191e8042 | ||
![]() |
18aebaa4a1 | ||
![]() |
34f1be198e | ||
![]() |
97aa976c97 | ||
![]() |
2ddb526323 | ||
![]() |
a7bb4a840f | ||
![]() |
feb54f024b | ||
![]() |
e74f4a00e2 | ||
![]() |
ef837a1018 | ||
![]() |
6d8f776200 | ||
![]() |
01cd2050d2 | ||
![]() |
edc122f63c | ||
![]() |
3e678b8568 | ||
![]() |
7c45ddb558 | ||
![]() |
e88a120a4e | ||
![]() |
486184e767 | ||
![]() |
823df19593 | ||
![]() |
0d53db7877 | ||
![]() |
f759ab1394 | ||
![]() |
9bc25e3647 | ||
![]() |
461bb7990a | ||
![]() |
bfb43bf7a9 | ||
![]() |
5706f46b00 | ||
![]() |
6d02684036 | ||
![]() |
16d286538f | ||
![]() |
4a74c65019 | ||
![]() |
b356877a23 | ||
![]() |
113fd7727d | ||
![]() |
bcad8f7ef3 | ||
![]() |
47a841fbcd | ||
![]() |
c45950a771 | ||
![]() |
d56e06fec7 | ||
![]() |
fcf105aaab | ||
![]() |
a48fd8c0a3 | ||
![]() |
e4333397e4 | ||
![]() |
022741dd90 | ||
![]() |
98e0554944 | ||
![]() |
d333738b0a | ||
![]() |
00c48f172b | ||
![]() |
4eb7d0d383 | ||
![]() |
9e811e882c | ||
![]() |
125e1384c1 | ||
![]() |
55b9231d47 | ||
![]() |
213a14b0c8 | ||
![]() |
d69e6d43fe | ||
![]() |
99cb0e92df | ||
![]() |
d681c5ca91 | ||
![]() |
5372279fb2 | ||
![]() |
2501b5cdea | ||
![]() |
116d485379 | ||
![]() |
c7f017191e | ||
![]() |
0af7379ed6 | ||
![]() |
9f92b014f4 | ||
![]() |
eb8985b2fd | ||
![]() |
49bc20ee95 | ||
![]() |
880947afe8 | ||
![]() |
9d03ce83d0 | ||
![]() |
284a028e1c | ||
![]() |
5a430bc812 | ||
![]() |
985cfa2b4d | ||
![]() |
51be588a74 | ||
![]() |
c93305ae11 | ||
![]() |
ff99a4639d | ||
![]() |
06a4a0bfeb | ||
![]() |
29d596baf7 | ||
![]() |
018fa8cc1e | ||
![]() |
1d85346770 | ||
![]() |
f8e2230b04 | ||
![]() |
550b0633b0 | ||
![]() |
5bedffc864 | ||
![]() |
4124a2db50 | ||
![]() |
47b29768c7 | ||
![]() |
1a71145b4d | ||
![]() |
46b0fb6dc5 | ||
![]() |
17f90d6d10 | ||
![]() |
b0de4027e3 | ||
![]() |
f0b041cdb1 | ||
![]() |
964b1c9fcf | ||
![]() |
60a62d867c | ||
![]() |
ce78d7a4c9 | ||
![]() |
2f34d8cb4e | ||
![]() |
e2f4f11e6a | ||
![]() |
31abc3eaa0 | ||
![]() |
6a3c55cc69 | ||
![]() |
47e7c495d7 | ||
![]() |
87955288a0 | ||
![]() |
fc58d3552a | ||
![]() |
ce865da613 | ||
![]() |
879cd84ccc | ||
![]() |
8ec8e394b8 | ||
![]() |
156b84acee | ||
![]() |
ec8808735b | ||
![]() |
7857c70b6e | ||
![]() |
22a76f489d | ||
![]() |
3520f04b36 | ||
![]() |
7ba9aa1c3d | ||
![]() |
cba32c1040 | ||
![]() |
12ff85aa2f | ||
![]() |
28205fe69e | ||
![]() |
ee7a01675f | ||
![]() |
54a50d62ff | ||
![]() |
dec75be45e | ||
![]() |
1b002bd49d | ||
![]() |
6d05150ae8 | ||
![]() |
fbcb369aa8 | ||
![]() |
6bdc99da1a | ||
![]() |
4163687acb | ||
![]() |
9de7d0f38d | ||
![]() |
da7c243fb0 | ||
![]() |
97065b70fa | ||
![]() |
b4568e52c3 | ||
![]() |
c58e412208 | ||
![]() |
2540818665 | ||
![]() |
ccf2c303b9 | ||
![]() |
3733473115 | ||
![]() |
6f053b6327 | ||
![]() |
5a70f506d3 | ||
![]() |
8f78669997 | ||
![]() |
e5c06e7491 | ||
![]() |
74f6638227 | ||
![]() |
9a3225e387 | ||
![]() |
105d81f7a6 | ||
![]() |
5bf425d2c4 | ||
![]() |
79fac4cb45 | ||
![]() |
50a7373d00 | ||
![]() |
1c29092af8 | ||
![]() |
ff04e23b64 | ||
![]() |
c7f0acb0df | ||
![]() |
3ab14ffc42 | ||
![]() |
699823d578 | ||
![]() |
c785a184c1 | ||
![]() |
63d8114791 | ||
![]() |
f84af3fcd7 | ||
![]() |
cac59fbc12 | ||
![]() |
fb0e44f4a6 | ||
![]() |
947ee8455c | ||
![]() |
5016298b45 | ||
![]() |
72c699f20b | ||
![]() |
77263e40b7 | ||
![]() |
3085579915 | ||
![]() |
35414daa20 | ||
![]() |
4aadd43248 | ||
![]() |
a3383e5617 | ||
![]() |
40b47a2621 | ||
![]() |
d15721c46d | ||
![]() |
5a1f8350cb | ||
![]() |
e355d41f60 | ||
![]() |
ca4b5ae016 | ||
![]() |
a72342431f | ||
![]() |
82d8192700 | ||
![]() |
5253e2084a | ||
![]() |
c4fc859789 | ||
![]() |
4137c52598 | ||
![]() |
2801198691 | ||
![]() |
3668f5099f | ||
![]() |
1f3b090f81 | ||
![]() |
77fda9d42c | ||
![]() |
ed82e28109 | ||
![]() |
3c30c52f01 | ||
![]() |
d075423ffb | ||
![]() |
48b288280d | ||
![]() |
b45be18ad3 | ||
![]() |
58f8cd0e03 | ||
![]() |
b101b492d4 | ||
![]() |
bc40109e61 | ||
![]() |
d6dae4871e | ||
![]() |
0936c326f8 | ||
![]() |
6f65c6ef26 | ||
![]() |
fd26e39acc | ||
![]() |
3127b41f38 | ||
![]() |
9b188dea72 | ||
![]() |
e614100c47 | ||
![]() |
e98e51a0ea | ||
![]() |
7cf2d42dcd | ||
![]() |
c59e0e95f9 | ||
![]() |
273f7fce8d | ||
![]() |
16f911e126 | ||
![]() |
89b8f01cda | ||
![]() |
d2512f2842 | ||
![]() |
90ca38ee7e | ||
![]() |
4a42a4f1ce | ||
![]() |
4d252a96f7 | ||
![]() |
4e94d7f04e | ||
![]() |
c93149a3a0 | ||
![]() |
248e09c573 | ||
![]() |
82d7bfad61 | ||
![]() |
60c98a5ba0 | ||
![]() |
fa8a63606c | ||
![]() |
efd055c2b0 | ||
![]() |
827197dbb7 | ||
![]() |
a5a0eab24c | ||
![]() |
782750d728 | ||
![]() |
a980f4efc1 | ||
![]() |
75148ec540 | ||
![]() |
d8f3ed7edb | ||
![]() |
c97526a36c | ||
![]() |
cae8264832 | ||
![]() |
6281fed309 | ||
![]() |
ebfd82eac6 | ||
![]() |
cc6415fa44 | ||
![]() |
c5d3ef47e7 | ||
![]() |
d3e89c2adc | ||
![]() |
328ae52c2c | ||
![]() |
9dfdf608dc | ||
![]() |
e6b61f667e | ||
![]() |
56488b567e | ||
![]() |
bf907d5f87 | ||
![]() |
b3f87b180e | ||
![]() |
9dbbf856d3 | ||
![]() |
66361cb6f3 | ||
![]() |
fa5fe8a325 | ||
![]() |
6593844ed0 | ||
![]() |
958d736e0c | ||
![]() |
faa776a8e5 | ||
![]() |
39d467314a | ||
![]() |
9890fb88aa | ||
![]() |
ff9ff71cb2 | ||
![]() |
fe3a25682a | ||
![]() |
8f874d6f32 | ||
![]() |
ba2b207dd6 | ||
![]() |
9f446a08d4 | ||
![]() |
b5fd757db4 | ||
![]() |
de85106c42 | ||
![]() |
f8f9cddafe | ||
![]() |
5c4126bc0a | ||
![]() |
dc94252f20 | ||
![]() |
08fc562eac | ||
![]() |
80159888fb | ||
![]() |
3d8c8a2f47 | ||
![]() |
23420c9f60 | ||
![]() |
ecf4fda285 | ||
![]() |
06099bf881 | ||
![]() |
277147acc0 | ||
![]() |
86c74737f5 | ||
![]() |
50ee5c766f | ||
![]() |
db868d0e3a | ||
![]() |
dab91974c1 | ||
![]() |
761030d721 | ||
![]() |
af43b394c3 | ||
![]() |
77be32167f | ||
![]() |
3547039cba | ||
![]() |
8d5d6a3ee6 | ||
![]() |
08c45c08d4 | ||
![]() |
e8b5587b99 | ||
![]() |
bbf7791893 | ||
![]() |
47b0519117 | ||
![]() |
787ea543b4 | ||
![]() |
ccea6a030c | ||
![]() |
5d7e1dd226 | ||
![]() |
a3784d7930 | ||
![]() |
05ba89888f | ||
![]() |
2a450bf3cc | ||
![]() |
e4b4611856 | ||
![]() |
6eab251438 | ||
![]() |
0c27b81993 | ||
![]() |
f287887e87 | ||
![]() |
7a08fc400a | ||
![]() |
de43978382 | ||
![]() |
4b14e7c645 | ||
![]() |
fcce25df48 | ||
![]() |
4619e57968 | ||
![]() |
c7dcbfbd7e | ||
![]() |
d9d9e5c5d0 | ||
![]() |
4bb9d2dd51 | ||
![]() |
14ebd73d80 | ||
![]() |
8f5ab18fa3 | ||
![]() |
88a5c8fea0 | ||
![]() |
9db8adea2a | ||
![]() |
46066fee0d | ||
![]() |
9caf77dd0c | ||
![]() |
fc00ed0239 | ||
![]() |
dedab599d6 | ||
![]() |
8dbd9db17b | ||
![]() |
bc87faa8d9 | ||
![]() |
512d2c8c32 | ||
![]() |
29ed57e955 | ||
![]() |
af1590cf60 | ||
![]() |
1cd0c08ab0 | ||
![]() |
b2709e2eb7 | ||
![]() |
d2c4e7c8e8 | ||
![]() |
1a4a621fd1 | ||
![]() |
14b1ea1896 | ||
![]() |
c1c7c9ec74 | ||
![]() |
eece7f91ac | ||
![]() |
3a0b084449 | ||
![]() |
b41022ca91 | ||
![]() |
79216341e3 | ||
![]() |
b57a7a8013 | ||
![]() |
22547099bd | ||
![]() |
24aec9f466 | ||
![]() |
5a12e30996 | ||
![]() |
48ebe38e08 | ||
![]() |
3f3e973f39 | ||
![]() |
5aafbbbff0 | ||
![]() |
7ac8e29f67 | ||
![]() |
ef49a73e4a | ||
![]() |
5172c38913 | ||
![]() |
21fcdf8693 | ||
![]() |
79bd5b9827 | ||
![]() |
8dbe77f4d0 | ||
![]() |
56678aa7f5 | ||
![]() |
e048b897da | ||
![]() |
875de844fb | ||
![]() |
1895010188 | ||
![]() |
5a58f77a32 | ||
![]() |
301f8910fb | ||
![]() |
c99ad39c56 | ||
![]() |
0a02e7bfaa | ||
![]() |
1c3ea91011 | ||
![]() |
8dc8b27cd0 | ||
![]() |
8da4689fea | ||
![]() |
1ba73dd47b | ||
![]() |
f8bb17e27f | ||
![]() |
b5b4357a5d | ||
![]() |
fa724b5abb | ||
![]() |
ef4b4ff795 | ||
![]() |
8dc043c44f | ||
![]() |
c0224c4d28 | ||
![]() |
7c6886857c | ||
![]() |
42926ec09d | ||
![]() |
325c7b48ed | ||
![]() |
92a4f7ad24 | ||
![]() |
f15281dcd0 | ||
![]() |
209fe60c76 | ||
![]() |
e8f15b27e3 | ||
![]() |
1c6d519162 | ||
![]() |
e92bf7b940 | ||
![]() |
b916aea3c1 | ||
![]() |
d69c315023 | ||
![]() |
d193a332bf | ||
![]() |
528db20161 | ||
![]() |
026b278311 | ||
![]() |
0050eeb89a | ||
![]() |
2b82d6211f | ||
![]() |
76a5231498 | ||
![]() |
418b0b22b6 | ||
![]() |
d387a4f790 | ||
![]() |
e997b4d2d1 | ||
![]() |
4ffcc5b17a | ||
![]() |
df272501c0 | ||
![]() |
d7de5e7a67 | ||
![]() |
b40a6a50fc | ||
![]() |
247ddee578 | ||
![]() |
b3399a4c01 | ||
![]() |
6f2c246146 | ||
![]() |
4de57d4014 | ||
![]() |
382cbc4798 | ||
![]() |
a2086cd09c | ||
![]() |
8e7a5369cc | ||
![]() |
814d330b8a | ||
![]() |
7cd19b3d67 | ||
![]() |
93cd24caa7 | ||
![]() |
a6fc7fd5c5 | ||
![]() |
0804c5ef24 | ||
![]() |
6d798a136f | ||
![]() |
87f69aebc9 | ||
![]() |
14f6ba098a | ||
![]() |
ef29f181fe | ||
![]() |
1f0d0d62b0 | ||
![]() |
68f4308664 | ||
![]() |
f4526238f3 | ||
![]() |
3953c17130 | ||
![]() |
99ed43f5c4 | ||
![]() |
4fb04b8f53 | ||
![]() |
89578efd68 | ||
![]() |
12d61ace2c | ||
![]() |
932ff99c58 | ||
![]() |
a0ab6931d1 | ||
![]() |
cf642b75d1 | ||
![]() |
7d7b11f9b8 | ||
![]() |
7b583f3b41 | ||
![]() |
0b03702152 | ||
![]() |
71e0be33f7 | ||
![]() |
2027012379 | ||
![]() |
502dd9a3db | ||
![]() |
0b31971678 | ||
![]() |
2f0d2e3ade | ||
![]() |
ab69258633 | ||
![]() |
78ae966813 | ||
![]() |
6af5b6c2ac | ||
![]() |
0311969060 | ||
![]() |
a2a7ef91bb | ||
![]() |
5d352d90c9 | ||
![]() |
209909ce94 | ||
![]() |
8a44eb7f62 | ||
![]() |
0725190a94 | ||
![]() |
fc4a23e148 | ||
![]() |
9fc0adbbe6 | ||
![]() |
0a4af74ea9 | ||
![]() |
d299a99a57 | ||
![]() |
a34e90e868 | ||
![]() |
aa558f0706 | ||
![]() |
0515403716 | ||
![]() |
1101c06130 | ||
![]() |
d148f46d30 | ||
![]() |
a52f9ef3f4 | ||
![]() |
597bb77a98 | ||
![]() |
e0c93228ee | ||
![]() |
203a80a430 | ||
![]() |
9ac94db8b3 | ||
![]() |
b1aab8cd0f | ||
![]() |
44f5e22bdc | ||
![]() |
a7083105b6 | ||
![]() |
37d6d47462 | ||
![]() |
b4d5f3becc | ||
![]() |
3a7975a235 | ||
![]() |
9839f65a69 | ||
![]() |
8dee34d6dc | ||
![]() |
d729ab940f | ||
![]() |
f3c925a857 | ||
![]() |
cc276a6862 | ||
![]() |
e1ae0158a4 | ||
![]() |
e5589d435c | ||
![]() |
c4864c214b | ||
![]() |
8431dce0b9 | ||
![]() |
959e8e36bb | ||
![]() |
68ba4dcc65 | ||
![]() |
3277ba4127 | ||
![]() |
ced1e20ecc | ||
![]() |
9a6495dc21 | ||
![]() |
eb313d6f65 | ||
![]() |
ab36283b2e | ||
![]() |
0c718c8658 | ||
![]() |
e52496ae76 | ||
![]() |
7ce2dc9056 | ||
![]() |
b2db5f6b43 | ||
![]() |
2741996002 | ||
![]() |
5b38d06bcb | ||
![]() |
6033abbdd9 | ||
![]() |
7bec7ce5a0 | ||
![]() |
162061259a | ||
![]() |
6eaa0f5fa6 | ||
![]() |
48f9d10e2a | ||
![]() |
dc42f63204 | ||
![]() |
86043aa89e | ||
![]() |
c4ba41c4b8 | ||
![]() |
866593343d | ||
![]() |
9f7c530922 | ||
![]() |
cfd7d1042d | ||
![]() |
4aac986e94 | ||
![]() |
209bdf2b06 | ||
![]() |
a786944ed6 | ||
![]() |
a6a5aec76f | ||
![]() |
5b1a2d1b28 | ||
![]() |
c9b0a05ee7 | ||
![]() |
2945c42ba2 | ||
![]() |
f82cb32a54 | ||
![]() |
455e9f9c91 | ||
![]() |
ed30911ba7 | ||
![]() |
9b820287d6 | ||
![]() |
abac11d959 | ||
![]() |
124d0146c8 | ||
![]() |
083c1c59f8 | ||
![]() |
2b899aeb93 | ||
![]() |
3bee20dd8a | ||
![]() |
86e78f1e5d | ||
![]() |
ed9b234c21 | ||
![]() |
1c44bf900e | ||
![]() |
4c8eb2c112 | ||
![]() |
a111e20f27 | ||
![]() |
b78e73a32b | ||
![]() |
bf40e7e152 | ||
![]() |
2071512b20 | ||
![]() |
e9fc38ddd1 | ||
![]() |
16634cf023 | ||
![]() |
2d541f9529 | ||
![]() |
da431c06bc | ||
![]() |
ecffdce786 | ||
![]() |
782f66cd49 | ||
![]() |
33893e40b8 | ||
![]() |
2c8abba79c | ||
![]() |
32a68c2d4d | ||
![]() |
1abcfe0c83 | ||
![]() |
4b247628e7 | ||
![]() |
4085a4053c | ||
![]() |
98a1290cbc | ||
![]() |
f7d9089ba7 | ||
![]() |
e7e9c35d69 | ||
![]() |
29519b0ebd | ||
![]() |
b99202f0ec | ||
![]() |
9cb1a52fc0 | ||
![]() |
0bdaf053f0 | ||
![]() |
d8ce093e9f | ||
![]() |
8bc815e827 | ||
![]() |
2e41cef16b | ||
![]() |
7ae2f3d1ec | ||
![]() |
8a36d0863a | ||
![]() |
d947059d5c | ||
![]() |
1e20fd5761 | ||
![]() |
d11abeb781 | ||
![]() |
fde74e1254 | ||
![]() |
4f2035aeb9 | ||
![]() |
d3f0d326b2 | ||
![]() |
ed509d0ae3 | ||
![]() |
6b1af72038 | ||
![]() |
11c5087a83 | ||
![]() |
4cada84ed5 | ||
![]() |
c071c6ff3e | ||
![]() |
d3b048085d | ||
![]() |
bc742aab0d | ||
![]() |
116a2521df | ||
![]() |
f7a5937ed5 | ||
![]() |
f92da9b14d | ||
![]() |
16a34c1d0c | ||
![]() |
b4f760f1b5 | ||
![]() |
2baa85c082 | ||
![]() |
be4574840a | ||
![]() |
6795ef04a8 | ||
![]() |
5f153a4912 | ||
![]() |
848a244c39 | ||
![]() |
c32a8f39ee | ||
![]() |
85d6fc9e57 | ||
![]() |
de3a2f4b33 | ||
![]() |
1fe9daafde | ||
![]() |
64e8b03f37 | ||
![]() |
c243d71e71 | ||
![]() |
c5b1906c47 | ||
![]() |
2aae735960 | ||
![]() |
b9edb1de15 | ||
![]() |
a96a80ce66 | ||
![]() |
7f4bfd4a60 | ||
![]() |
ff28c1657e | ||
![]() |
18408f6ab3 | ||
![]() |
858b5d1cf7 | ||
![]() |
ebb7fe8ca8 | ||
![]() |
4d40ec72ad | ||
![]() |
c5cd72a631 | ||
![]() |
428f7e9d1c | ||
![]() |
7bc146bcce | ||
![]() |
803c5b6ee0 | ||
![]() |
259857453a | ||
![]() |
6c08ebaea3 | ||
![]() |
a07cfaaeac | ||
![]() |
6b54932427 | ||
![]() |
08b375df48 | ||
![]() |
b51acbf615 | ||
![]() |
535d6569fc | ||
![]() |
93e14d8f74 | ||
![]() |
111953a1bf | ||
![]() |
7bb3aa250e | ||
![]() |
1869f09a4c | ||
![]() |
25db64527c | ||
![]() |
a6f12af8da | ||
![]() |
2462e4f33d | ||
![]() |
cb9b4b8013 | ||
![]() |
810692e2c1 | ||
![]() |
38b65ff9ea | ||
![]() |
91bd68e1f3 | ||
![]() |
88316e78fb | ||
![]() |
b08eac1a7d | ||
![]() |
dd3d4c3066 | ||
![]() |
fe7576ee66 | ||
![]() |
2130460ef7 | ||
![]() |
6d12b3d5a9 | ||
![]() |
51b98ba370 | ||
![]() |
83f2aec1e3 | ||
![]() |
6fd0e88dd5 | ||
![]() |
e2a7dba29f | ||
![]() |
1e8bece479 | ||
![]() |
5cfa492d7d | ||
![]() |
fc4f3c5157 | ||
![]() |
960031ca4f |
@ -1,4 +1,6 @@
|
||||
node_modules
|
||||
radata
|
||||
stats.radata
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: amark
|
||||
patreon: gunDB
|
||||
open_collective: gun
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
114
.github/workflows/ci.yml
vendored
Normal file
114
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [ 14.x ]
|
||||
os: [ ubuntu-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
# checkout the code
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# verify the version in package.json matches the release tag
|
||||
- name: Version
|
||||
uses: tcurdt/action-verify-version-npm@master
|
||||
|
||||
# setup the node version
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
# cache node_modules if we can
|
||||
- name: Cache
|
||||
id: cache-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ matrix.node-version }}-${{ runner.OS }}-build-${{ hashFiles('package.json') }}
|
||||
|
||||
# ottherweise run install
|
||||
- name: Install
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
|
||||
# run tests
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
# create github release
|
||||
release:
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [ test ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# create github release (which triggers the release workflows)
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
|
||||
# # publish latest master or release to dockerhub
|
||||
# dockerhub:
|
||||
# if: github.event_name == 'push'
|
||||
# needs: [ test ]
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# image: ${{ secrets.DOCKERHUB_USERNAME }}/gun
|
||||
# steps:
|
||||
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v2
|
||||
|
||||
# - name: Login
|
||||
# env:
|
||||
# DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
# DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
# run: echo -n ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
|
||||
|
||||
# - name: Build
|
||||
# run: |
|
||||
# echo "SHA=$GITHUB_SHA"
|
||||
# docker build --build-arg SHA=$GITHUB_SHA \
|
||||
# BUILD_DATE=$(date +'%Y-%m-%dT%H:%M:%S') \
|
||||
# VCS_REF=${GITHUB_REF} \
|
||||
# VCS_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} \
|
||||
# VERSION=${GITHUB_REF##*/} \
|
||||
# SHA=$GITHUB_SHA \
|
||||
# --label "SHA=$GITHUB_SHA" \
|
||||
# --tag ${{ env.image }}:${GITHUB_REF##*/} \
|
||||
# --tag ${{ env.image }}:latest \
|
||||
# .
|
||||
|
||||
# - name: Push
|
||||
# run: docker push ${{ env.image }}
|
||||
|
||||
|
||||
# publish release to npm
|
||||
npm:
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [ test ]
|
||||
# needs: [ release ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
||||
run: |
|
||||
npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN
|
||||
npm install
|
||||
npm publish --access=public
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -7,7 +7,15 @@ yarn.lock
|
||||
.idea/
|
||||
*.bak
|
||||
*.new
|
||||
*.log
|
||||
v8.json
|
||||
*.DS_store
|
||||
isolate*.log
|
||||
.esm-cache
|
||||
.sessionStorage
|
||||
.localStorage
|
||||
/types/**/*.ts
|
||||
!/types/**/*.d.ts
|
||||
!/types/**/*.test-d.ts
|
||||
/gun.ts
|
||||
/temp/
|
5
.npmignore
Normal file
5
.npmignore
Normal file
@ -0,0 +1,5 @@
|
||||
*.ts
|
||||
/temp/
|
||||
!*.d.ts
|
||||
*.radata
|
||||
isolate-*
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
*
|
@ -2,9 +2,9 @@ language: node_js
|
||||
branches:
|
||||
except:
|
||||
- debug
|
||||
- manhattan
|
||||
node_js:
|
||||
- 8
|
||||
- 10
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- node_modules
|
||||
|
30
CHANGELOG.md
30
CHANGELOG.md
@ -1,20 +1,26 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.2020.x
|
||||
|
||||
`>0.2020.520` may break in-process `gun1` `gun2` message passing. Check `test/common.js` "Check multi instance message passing" for a hint and/or complain on community chat.
|
||||
|
||||
- No breaking changes to core API.
|
||||
- Storage adapter `put` event breaking change (temporary?), RAD is official now and storage adapters should be RAD plugins instead of GUN adapters.
|
||||
- GUN soul format changed from being a random UUID to being a more predictable graph path (of where initially created) to support even better offline behavior. This means `null`ing & replacing an object will not create a new but re-merge.
|
||||
- Pretty much all internal GUN utility will be deleted, these are mostly undocumented but will affect some people - they will still be available as a separate file but deprecated.
|
||||
- As the DHT gets implemented, your relay peers may automatically connect to it, so do not assume your peer is standalone. `Gun({axe: false` should help prevent this but loses you most scaling properties.
|
||||
- The 2019 -> 2020 "changes" are happening gradually, based on experimental in-production tests.
|
||||
- As always, **most important** is to ask in the [community chat](http://chat.gun.eco) if you have any issues, and to keep up to date with changes.
|
||||
|
||||
## 0.2019.x
|
||||
|
||||
Some RAD & SEA data format changes, but with as much backward compatibility as possible, tho ideally should be dropped.
|
||||
|
||||
## 0.9.x
|
||||
|
||||
No breaking changes, but the new Radix Storage Engine (RSE) has been finally integrated and works with S3 as a backup. Expect an order of magnitude or more in cost savings, we'll report our December bill compared to November when we get it.
|
||||
No breaking changes, but the new Radix Storage Engine (RSE) has been finally integrated and works with S3 as a backup.
|
||||
|
||||
We have successfully benchmarked it against **1,000,000 records** doing end-to-end triple verification using our Jepsen-inspired [PANIC](https://github.com/gundb/panic-server) distritubed testing framework, doing **~4K acked writes/second** on a Macbook Air dev machine. For more information on PANIC, check out this [5 minute presentation](https://youtu.be/nTbUCTgLmkY) we did in Sweden (and the prior talk, for those interested in [porting GUN](https://github.com/amark/gun/wiki/porting-gun) out of its reference implementation of JS).
|
||||
|
||||
> Warning: There is a known rare edge case in RSE currently, if data is split between two chunked files, a GET will only return from the first chunk. This will be fixed soon, but we still encourage developers to run and test against it, please report any problems.
|
||||
|
||||
To use RSE, initialize a gun server peer with the default storage disabled, like `Gun({localStorage: false})`. Want to use it with S3? All you need to do is make sure that your environment variables are configured and it will automatically use S3, here is a [template](https://github.com/amark/gun/wiki/Using-Amazon-S3-for-Storage). This works especially well for our 1-click-deploy Heroku [demo server](http://gunjs.herokuapp.com/) with the example apps.
|
||||
|
||||
Finally, with **end-to-end encryption** being enabled with our Security, Encryption, Authorization (SEA) framework (check out our [P2P/decentralized crypto-identity blockchain](https://github.com/amark/gun/wiki/auth)), gun is marching towards a stable v1.0 production-ready system (it is already being used in production by a Northern European government's Navy). So if you are able to [work around the remaining bugs](https://github.com/amark/gun/issues), we would appreciate everybody efforts in experimenting and testing out gun and reporting any last hiccups **in our lead up to the v1.0**!
|
||||
|
||||
We will be **overhauling documentation in this v0.9.x series**, please make complaints about what is missing, and how we can make it better, so it will be polished for the v1.0! The [chatroom is actively and friendly for help](https://gitter.im/amark/gun), [StackOverflow](https://stackoverflow.com/questions/tagged/gun) for questions. And we're looking for [sponsors](https://www.patreon.com/gunDB), we **regularly get 1,200+ uniques every 2 weeks** on this repo, we've had **53% monthly growth** on our installs, and GUN is ranked in the top quarter of the top 1% of the top 1% fastest growing projects across all GitHub! If you are an Enterprise, this would be a great time to chat with us about our IoT, AI/ML, edge computing, graph, and cybersecurity solutions.
|
||||
|
||||
Here is towards a v1.0! Cheers.
|
||||
// Edit: commentary removed.
|
||||
|
||||
## 0.8.x
|
||||
|
||||
|
29
Dockerfile
29
Dockerfile
@ -1,5 +1,15 @@
|
||||
FROM alpine:latest
|
||||
# Build-time metadata as defined at http://label-schema.org
|
||||
# install packages
|
||||
FROM node:lts-alpine as builder
|
||||
RUN mkdir /work
|
||||
WORKDIR /work
|
||||
RUN apk add --no-cache alpine-sdk python3
|
||||
COPY package*.json ./
|
||||
RUN mkdir -p node_modules
|
||||
RUN npm ci --only=production
|
||||
|
||||
# fresh image without dev packages
|
||||
FROM node:lts-alpine
|
||||
# build-time metadata as defined at http://label-schema.org
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
ARG VCS_URL
|
||||
@ -12,15 +22,14 @@ LABEL org.label-schema.build-date=$BUILD_DATE \
|
||||
org.label-schema.vendor="The Gun Database Team" \
|
||||
org.label-schema.version=$VERSION \
|
||||
org.label-schema.schema-version="1.0"
|
||||
# org.label-schema.description="Let it be pulled from Readme.md..." \
|
||||
WORKDIR /app
|
||||
ARG SHA
|
||||
RUN mkdir /work
|
||||
WORKDIR /work
|
||||
COPY --from=builder /work/node_modules ./node_modules
|
||||
RUN npm rebuild -q
|
||||
ADD . .
|
||||
ENV NPM_CONFIG_LOGLEVEL warn
|
||||
RUN apk update && apk upgrade \
|
||||
&& apk add --no-cache ca-certificates nodejs-npm \
|
||||
&& apk add --no-cache --virtual .build-dependencies python make g++ \
|
||||
&& npm install \
|
||||
&& apk del .build-dependencies && rm -rf /var/cache/* /tmp/npm*
|
||||
RUN echo "{ \"sha\": \"$SHA\" }" > version.json
|
||||
RUN cat version.json
|
||||
EXPOSE 8080
|
||||
EXPOSE 8765
|
||||
CMD ["npm","start"]
|
||||
|
2
Procfile
2
Procfile
@ -1 +1 @@
|
||||
web: node --optimize_for_size --gc_interval=100 examples/http.js
|
||||
web: node --inspect examples/http.js
|
207
README.md
207
README.md
@ -1,64 +1,73 @@
|
||||
<p><a href="https://gun.eco/"><img width="40%" src="https://cldup.com/TEy9yGh45l.svg"/></a><img width="50%" align="right" vspace="25" src="https://gun.eco/see/demo.gif"/></p>
|
||||
<p id="readme"><a href="https://gun.eco/"><img width="40%" src="https://cldup.com/TEy9yGh45l.svg"/></a><img width="50%" align="right" vspace="25" src="https://gun.eco/see/demo.gif"/></p>
|
||||
|
||||
[](https://www.npmjs.com/package/gun)
|
||||
[](https://travis-ci.org/amark/gun)
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Famark%2Fgun?ref=badge_shield)
|
||||
[](https://gitter.im/amark/gun)
|
||||
[](https://www.jsdelivr.com/package/npm/gun)
|
||||
[](https://www.jsdelivr.com/package/npm/gun)
|
||||

|
||||
[](http://chat.gun.eco)
|
||||
|
||||
**GUN** is an _ecosystem_ of tools that let you <u>build tomorrow's dApps, today</u>.
|
||||
**GUN** is an [ecosystem](https://gun.eco/docs/Ecosystem) of **tools** that let you build [community run](https://www.nbcnews.com/tech/tech-news/these-technologists-think-internet-broken-so-they-re-building-another-n1030136) and [encrypted applications](https://gun.eco/docs/Cartoon-Cryptography) - like an Open Source Firebase or a Decentralized Dropbox.
|
||||
|
||||
Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
|
||||
The [Internet Archive](https://news.ycombinator.com/item?id=17685682) and [100s of other apps](https://github.com/amark/gun/wiki/awesome-gun) run GUN in-production.
|
||||
|
||||
+ Multiplayer by default with realtime p2p state synchronization!
|
||||
+ Graph data lets you use key/value, tables, documents, videos, & more!
|
||||
+ Local-first, offline, and decentralized with end-to-end encryption.
|
||||
|
||||
Decentralized alternatives to [Zoom](https://www.zdnet.com/article/era-hatches-meething-an-open-source-browser-based-video-conferencing-system/), [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [Instagram](https://iris.to/), [Slack](https://iris.to/), [YouTube](https://d.tube/), [Stripe](https://twitter.com/marknadal/status/1422717427427647489), [Wikipedia](https://news.ycombinator.com/item?id=17685682), Facebook [Horizon](https://twitter.com/marknadal/status/1424476179189305347) and more have already pushed terabytes of daily P2P traffic on GUN. We are a [friendly community](http://chat.gun.eco/) creating a [free fun future for freedom](https://youtu.be/1HJdrBk3BlE):
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<a href=""><img width="31%" align="left" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
|
||||
<a href="https://github.com/cstefanache/cstefanache.github.io/blob/master/_posts/2016-08-02-gun-db-artificial-knowledge-sharing.md#gundb"><img width="31%" align="left" src="https://gun.eco/see/aiml.gif" title="AI/ML"/></a>
|
||||
<a href="http://gps.gunDB.io/"><img width="31%" align="left" src="https://gun.eco/see/gps.gif" title="GPS"/></a>
|
||||
<a href="https://github.com/lmangani/gun-scape#gun-scape"><img width="31%" align="left" src="https://gun.eco/see/dataviz.gif" title="Data Viz"/></a>
|
||||
<a href="https://github.com/amark/gun/wiki/Auth"><img width="31%" align="left" src="https://gun.eco/see/p2p.gif" title="P2P"/></a>
|
||||
<a href="https://github.com/Stefdv/gun-ui-lcd#okay-what-about-gundb-"><img width="31%" align="left" src="https://gun.eco/see/iot.gif" title="IoT"/></a>
|
||||
<a href="https://youtu.be/s_m16-w6bBI"><img width="31%" src="https://gun.eco/see/3dvr.gif" title="3D VR"/></a>
|
||||
<a href="https://github.com/cstefanache/cstefanache.github.io/blob/06697003449e4fc531fd32ee068bab532976f47b/_posts/2016-08-02-gun-db-artificial-knowledge-sharing.md"><img width="31%" src="https://gun.eco/see/aiml.gif" title="AI/ML"/></a>
|
||||
<a href="http://gps.gunDB.io/"><img width="31%" src="https://gun.eco/see/gps.gif" title="GPS"/></a>
|
||||
</tr>
|
||||
<tr>
|
||||
<a href="https://github.com/lmangani/gun-scape#gun-scape"><img width="31%" src="https://gun.eco/see/dataviz.gif" title="Data Viz"/></a>
|
||||
<a href="https://github.com/amark/gun/wiki/Auth"><img width="31%" src="https://gun.eco/see/p2p.gif" title="P2P"/></a>
|
||||
<a href="https://github.com/Stefdv/gun-ui-lcd#okay-what-about-gundb-"><img width="31%" src="https://gun.eco/see/iot.gif" title="IoT"/></a>
|
||||
</tr>
|
||||
<tr>
|
||||
<a href="http://chat.gun.eco"><img width="31%" src="https://gun.eco/see/vr-world.gif" title="VR World"/></a>
|
||||
<a href="https://youtu.be/1ASrmQ-CwX4"><img width="31%" src="https://gun.eco/see/ar.gif" title="AR"/></a>
|
||||
<a href="https://meething.space/"><img width="31%" src="https://gun.eco/see/video-conf.gif" title="Video Confernece"/></a>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The ecosystem is one nice stack of technologies that looks like this:
|
||||
|
||||
<div><img width="48%" src="https://gun.eco/see/stack.png"/>
|
||||
<img width="48%" align="right" src="https://gun.eco/see/layers.png"/></div>
|
||||
|
||||
For now, it is best to start with GUN and _just use it_ to learn the basics, since it is _**so easy**_: (**or** want to read more? Skip ahead to the "[What is GUN?](#what-is-gun)" section.)
|
||||
|
||||
## Quickstart
|
||||
|
||||
GUN is *super easy* to get started with:
|
||||
|
||||
- Try the [interactive tutorial](https://gun.eco/docs/Todo-Dapp) in the browser (**5min** ~ average developer).
|
||||
- Or `npm install gun` and run the examples with `cd node_modules/gun && npm start` (**5min** ~ average developer).
|
||||
|
||||
> **Note:** If you don't have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first.
|
||||
> If the `npm` command line didn't work, you may need to `mkdir node_modules` first or use `sudo`.
|
||||
|
||||
- An online demo of the examples are available here: http://gunjs.herokuapp.com/
|
||||
- Or write a quick app: ([try now in jsbin](http://jsbin.com/sovihaveso/edit?js,console))
|
||||
- An online demo of the examples are available here: http://try.axe.eco/
|
||||
- Or write a quick app: ([try now in a playground](https://jsbin.com/kadobamevo/edit?js,console))
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<script>
|
||||
// var Gun = require('gun'); // in NodeJS
|
||||
// var Gun = require('gun/gun'); // in React
|
||||
var gun = Gun();
|
||||
// import GUN from 'gun'; // in ESM
|
||||
// GUN = require('gun'); // in NodeJS
|
||||
// GUN = require('gun/gun'); // in React
|
||||
gun = GUN();
|
||||
|
||||
gun.get('mark').put({
|
||||
name: "Mark",
|
||||
email: "mark@gunDB.io",
|
||||
email: "mark@gun.eco",
|
||||
});
|
||||
|
||||
gun.get('mark').on(function(data, key){
|
||||
console.log("update:", data);
|
||||
gun.get('mark').on((data, key) => {
|
||||
console.log("realtime updates:", data);
|
||||
});
|
||||
|
||||
setInterval(() => { gun.get('mark').get('live').put(Math.random()) }, 9);
|
||||
</script>
|
||||
```
|
||||
- Or try something **mind blowing**, like saving circular references to a table of documents! ([play](http://jsbin.com/wefozepume/edit?js,console))
|
||||
```javascript
|
||||
var cat = {name: "Fluffy", species: "kitty"};
|
||||
var mark = {boss: cat};
|
||||
cat = {name: "Fluffy", species: "kitty"};
|
||||
mark = {boss: cat};
|
||||
cat.slave = mark;
|
||||
|
||||
// partial updates merge with existing data!
|
||||
@ -66,13 +75,13 @@ gun.get('mark').put(mark);
|
||||
|
||||
// access the data as if it is a document.
|
||||
gun.get('mark').get('boss').get('name').once(function(data, key){
|
||||
// `val` grabs the data once, no subscriptions.
|
||||
// `once` grabs the data once, no subscriptions.
|
||||
console.log("Mark's boss is", data);
|
||||
});
|
||||
|
||||
// traverse a graph of circular references!
|
||||
gun.get('mark').get('boss').get('slave').once(function(data, key){
|
||||
console.log("Mark is the slave!", data);
|
||||
console.log("Mark is the cat's slave!", data);
|
||||
});
|
||||
|
||||
// add both of them to a table!
|
||||
@ -85,21 +94,41 @@ gun.get('list').map().once(function(data, key){
|
||||
});
|
||||
|
||||
// live update the table!
|
||||
gun.get('list').set({type: "cucumber", goal: "scare cat"});
|
||||
gun.get('list').set({type: "cucumber", goal: "jumping cat"});
|
||||
```
|
||||
|
||||
Want to keep building more? **Jump to [THE DOCUMENTATION](#documentation)!**
|
||||
|
||||
# What is GUN?
|
||||
# About
|
||||
First & foremost, GUN is **a community of the nicest and most helpful people** out there. So [I want to invite you](http://chat.gun.eco) to come tell us about what **you** are working on & wanting to build (new or old school alike! Just be nice as well.) and ask us your questions directly. :)
|
||||
|
||||
First & foremost, GUN is **a community of the nicest and most helpful people** out there. So [I want to invite you](https://gitter.im/amark/gun) to come tell us about what **you** are working on & wanting to build (new or old school alike! Just be nice as well.) and ask us your questions directly. :)
|
||||
<p align="center"><a href="https://www.youtube.com/watch?v=oTQXzhm8w_8"><img width="250" src="https://img.youtube.com/vi/oTQXzhm8w_8/0.jpg"><br/>Watch the 100 second intro!</a></p>
|
||||
|
||||
The GUN ecosystem stack is a collection of independent and modular tools covering everything from [CRDT](https://crdt.tech/) [conflict resolution](https://gun.eco/distributed/matters.html), [cryptographic security](https://gun.eco/docs/Cartoon-Cryptography) & [encryption](https://gun.eco/docs/SEA), [radix storage serialization](https://gun.eco/docs/RAD), [mesh networking](https://gun.eco/docs/DAM) & [routing algorithms](https://gun.eco/docs/Routing), to distributed systems [correctness & load testing](https://github.com/gundb/panic-server), CPU scheduled [JSON parser](https://github.com/amark/gun/blob/master/lib/yson.js) to prevent UI lag, and more!
|
||||
|
||||
<div><img width="48%" src="https://gun.eco/see/stack.png"/>
|
||||
<img width="48%" align="right" src="https://gun.eco/see/layers.png"/></div>
|
||||
|
||||
On that note, let's get some official shout outs covered first:
|
||||
|
||||
### Support
|
||||
|
||||
<p align="center">
|
||||
Thanks to:<br/>
|
||||
Thanks to:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td vlign="center"><a href="https://mozilla.org/builders"><img height="100" src="https://user-images.githubusercontent.com/1423657/81992335-85346480-9643-11ea-8754-8275e98e06bc.png"></a></td>
|
||||
<td vlign="center"><a href="http://unstoppabledomains.com/"><img src="https://gun.eco/img/unstoppable.png"></a></td>
|
||||
<td vlign="center"><a href="https://mask.io/"><img src="https://dimensiondev.github.io/Mask-VI/assets/Logo/MB--Logo--CombH-Circle--Blue.svg" width="250"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td vlign="center"> <a href="https://www.ajar.org/"><img src="https://www.ajar.org/logo.png" height="120"></a></td>
|
||||
<td vlign="center"><a href="https://wallie.io/"><img src="https://raw.githubusercontent.com/gundb/gun-site/master/img/wallie.png" width="250"></a></td>
|
||||
<td vlign="center"> <a href="https://ghostdrive.com/"><img src="https://gun.eco/img/ghostdrive.png" height="120"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="https://github.com/robertheessels">Robert Heessels</a>,
|
||||
<a href="http://qxip.net/">Lorenzo Mangani</a>,
|
||||
<a href="https://nlnet.nl/">NLnet Foundation</a>,
|
||||
@ -113,13 +142,28 @@ Thanks to:<br/>
|
||||
<a href="http://github.com/alanmimms">Alan Mimms</a>,
|
||||
<a href="https://github.com/dfreire">Dário Freire</a>,
|
||||
<a href="http://github.com/velua">John Williamson</a>,
|
||||
<a href="http://github.com/finwo">Robin Bron</a>
|
||||
<a href="http://github.com/finwo">Robin Bron</a>,
|
||||
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>,
|
||||
<a href="http://github.com/mikestaub">Mike Staub</a>,
|
||||
<a href="http://github.com/bmatusiak">Bradley Matusiak</a>,
|
||||
<a href="https://github.com/sjuxax">Jeff Cook</a>,
|
||||
<a href="https://github.com/nmauersberg">Nico</a>,
|
||||
<a href="https://github.com/ajartille">Aaron Artille</a>,
|
||||
<a href="https://github.com/timjrobinson">Tim Robinson</a>,
|
||||
<a href="https://github.com/hibas123">Fabian Stamm</a>,
|
||||
<a href="https://twitter.com/mikestaub">Mike Staub</a>,
|
||||
<a href="https://hunterowens.com/">Hunter Owens</a>,
|
||||
<a href="https://github.com/JacobMillner">Jacob Millner</a>,
|
||||
<a href="https://github.com/b-lack">Gerrit Balindt</a>,
|
||||
<a href="https://github.com/gabriellemon">Gabriel Lemon</a>,
|
||||
<a href="https://github.com/murageyun">Murage Martin</a>,
|
||||
<a href="https://github.com/octalmage">Jason Stallings</a>
|
||||
</p>
|
||||
|
||||
- Join others in sponsoring code: https://www.patreon.com/gunDB !
|
||||
- Ask questions: http://stackoverflow.com/questions/tagged/gun ?
|
||||
- Found a bug? Report at: https://github.com/amark/gun/issues ;
|
||||
- **Need help**? Chat with us: https://gitter.im/amark/gun .
|
||||
- **Need help**? Chat with us: http://chat.gun.eco .
|
||||
|
||||
### History
|
||||
|
||||
@ -146,11 +190,11 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
|
||||
<tr>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/brysgo/graphql-gun">GraphQL</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/PenguinMan98/electrontest">Electron</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/React-Native">React Native</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/React-Native">React & Native</a></h3></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/sjones6/vue-gun">Vue</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/React-Tutorial">React</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Svelte">Svelte</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/Stefdv/gun-ui-lcd#syncing">Webcomponents</a></h3></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -160,54 +204,74 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Auth">Crypto Auth</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Awesome-GUN">Modules</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/Awesome-GUN">Modules</a></h3></td>
|
||||
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Roadmap">Roadmap</a></h3></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This would not be possible without **community contributors**, big shout out to:
|
||||
|
||||
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
|
||||
**[ajmeyghani](https://github.com/ajmeyghani) ([Learn GUN Basics with Diagrams](https://medium.com/@ajmeyghani/gundb-a-graph-database-in-javascript-3860a08d873c))**; **[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; **[rogowski](https://github.com/rogowski) (AXE)**; [sbeleidy](https://github.com/sbeleidy); **[sbiaudet](https://github.com/sbiaudet) ([C# Port](https://github.com/sbiaudet/cs-gun))**; **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Shadyzpop](https://github.com/Shadyzpop) ([React Native example](https://github.com/amark/gun/tree/master/examples/react-native))**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; RIP **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[xmonader](https://github.com/xmonader) ([Python Port](https://github.com/xmonader/pygundb))**;
|
||||
|
||||
I am missing many others, apologies, will be adding them soon!
|
||||
I am missing many others, apologies, will be adding them soon! This list is infinitely old & way out of date, if you want to be listed in it please make a PR! :)
|
||||
|
||||
## Testing
|
||||
|
||||
Tests may be run with `npm test`. Tests will trigger persistent writes to the DB, so subsequent runs of the test will fail. You must clear the DB before running the tests again. This can be done by running the following command in the project directory.
|
||||
You will need to `npm install -g mocha` first. Then in the gun root folder run `npm test`. Tests will trigger persistent writes to the DB, so subsequent runs of the test will fail. You must clear the DB before running the tests again. This can be done by running `rm -rf *data*` command in the project directory.
|
||||
|
||||
```bash
|
||||
rm -rf *data*
|
||||
```
|
||||
## Shims
|
||||
|
||||
### Additional Cryptography Libraries
|
||||
> These are only needed for NodeJS & React Native, they shim the native Browser WebCrypto API.
|
||||
|
||||
To install with npm, first install `npm install gun -S`.
|
||||
For just the networking layer, import Gun:
|
||||
If you want to use [SEA](https://gun.eco/docs/SEA) for `User` auth and security, you will need to install:
|
||||
|
||||
`npm install @peculiar/webcrypto --save`
|
||||
|
||||
Please see [our React Native docs](https://gun.eco/docs/React-Native) for installation instructions!
|
||||
|
||||
Then you can require [SEA](https://gun.eco/docs/SEA) without an error:
|
||||
|
||||
```javascript
|
||||
var Gun = require('gun/gun');
|
||||
GUN = require('gun/gun');
|
||||
SEA = require('gun/sea');
|
||||
```
|
||||
|
||||
If you also need to install SEA for user auth and crypto, also install some of its dependencies like this:
|
||||
|
||||
`npm install @trust/crypto text-encoding node-webcrypto-ossl --save`
|
||||
|
||||
You will need to require it too (it will be automatically added to the Gun object):
|
||||
|
||||
```javascript
|
||||
var Gun = require('gun/gun');
|
||||
var Sea = require('gun/sea');
|
||||
```
|
||||
|
||||
|
||||
## Deploy
|
||||
|
||||
To quickly spin up a Gun test server for your development team, utilize either [Heroku](http://heroku.com) or [Docker](http://docker.com) or any variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), [Flynn.io](http://flynn.io), [now.sh](https://zeit.co/now), etc. !
|
||||
> Note: The default examples that get auto-deployed on `npm start` CDN-ify all GUN files, modules, & storage.
|
||||
|
||||
> Note: Moving forward, AXE will start to automatically cluster your peer into a shared DHT. You may want to disable this to run an isolated network.
|
||||
|
||||
> Note: When deploying a web application using GUN on a cloud provider, you may have to set `CI=false` in your `.env`. This prevents GUN-specific warnings from being treated as errors when deploying your app. You may also resolve this by modifying your webpack config to not try to build the GUN dependencies.
|
||||
|
||||
To quickly spin up a GUN relay peer for your development team, utilize [Heroku](http://heroku.com), [Docker](http://docker.com), or any others listed below. Or some variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), K8s, etc. ! Or use all of them so your relays are decentralized too!
|
||||
|
||||
### Linux
|
||||
|
||||
`SSH` into the home directory of a clean OS install with `sudo` ability. Set any environment variables you need (see below), then do:
|
||||
|
||||
```bash
|
||||
curl -o- https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
|
||||
```
|
||||
|
||||
> Read [install.sh](https://github.com/amark/gun/blob/master/examples/install.sh) first!
|
||||
> If `curl` is not found, *copy&paste* the contents of install.sh into your ssh.
|
||||
|
||||
You can now safely `CTRL+A+D` to escape without stopping the peer. To stop everything `killall screen` or `killall node`.
|
||||
|
||||
Environment variables may need to be set like `export HTTPS_CERT=~/cert.pem HTTPS_KEY=~/key.pem PORT=443`. You can also look at a sample [nginx](https://gun.eco/docs/nginx) config. For production deployments, you probably will want to use something like `pm2` or better to keep the peer alive after machine reboots.
|
||||
|
||||
### [Dome](https://www.trydome.io/)
|
||||
[Deploy GUN in one-click](https://app.trydome.io/signup?package=gun) with [Dome](https://trydome.io) and receive a free trial:
|
||||
|
||||
[](https://app.trydome.io/signup?package=gun)
|
||||
|
||||
### [Heroku](https://www.heroku.com/)
|
||||
|
||||
[](https://heroku.com/deploy?template=https://github.com/amark/gun)
|
||||
|
||||
> Heroku deletes your data every 15 minutes, one way to fix this is by adding [cheap storage](https://gun.eco/docs/Using-Amazon-S3-for-Storage).
|
||||
|
||||
Or:
|
||||
|
||||
```bash
|
||||
@ -217,20 +281,19 @@ heroku create
|
||||
git push -f heroku HEAD:master
|
||||
```
|
||||
|
||||
Then visit the URL in the output of the 'heroku create' step, in a browser.
|
||||
Then visit the URL in the output of the 'heroku create' step, in a browser. Make sure to set any environment config vars in the settings tab.
|
||||
|
||||
### [Now.sh](https://zeit.co/now/)
|
||||
### [Zeet.co](https://www.zeet.co/)
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
now --npm amark/gun
|
||||
```
|
||||
[](https://deploy.zeet.co/?url=https://github.com/amark/gun)
|
||||
|
||||
Then visit the URL in the output of the 'now --npm' step, in your browser.
|
||||
|
||||
### [Docker](https://www.docker.com/)
|
||||
|
||||
[](https://hub.docker.com/r/gundb/gun/) [](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [](https://hub.docker.com/r/gundb/gun/) [](https://hub.docker.com/r/gundb/gun/)
|
||||
> Warning: Docker image is community contributed and may be old with missing security updates, please check version numbers to compare.
|
||||
|
||||
[](https://hub.docker.com/r/gundb/gun/) [](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [](https://hub.docker.com/r/gundb/gun/) [](https://hub.docker.com/r/gundb/gun/)
|
||||
|
||||
Pull from the [Docker Hub](https://hub.docker.com/r/gundb/gun/) [](https://microbadger.com/images/gundb/gun). Or:
|
||||
|
||||
|
25
RELEASE.md
Normal file
25
RELEASE.md
Normal file
@ -0,0 +1,25 @@
|
||||
Every push or pull request will
|
||||
|
||||
- run the tests
|
||||
|
||||
Every push to master will
|
||||
|
||||
- run the tests
|
||||
- publish the latest docker image to dockerhub
|
||||
|
||||
Creating a tag that starts with `v` will
|
||||
|
||||
- create a new github release
|
||||
- publish the release to npm
|
||||
- publish the release to dockerhub
|
||||
|
||||
Creating a release from the github web interface will
|
||||
|
||||
- publish the release to npm
|
||||
- publish the release to dockerhub
|
||||
|
||||
Creating the release for version `0.2021.001` from the command line works as follows
|
||||
|
||||
git tag v0.2021.001
|
||||
git push --tags
|
||||
|
47
SECURITY.md
Normal file
47
SECURITY.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Security Policy
|
||||
|
||||
## Introduction
|
||||
|
||||
Security is our top priority. We are committed to ensuring that our project is as secure as possible for everyone who uses it. This document outlines our security policy and procedures for dealing with security issues.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We provide security updates for the following versions of our project:
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.2020.x| :white_check_mark: |
|
||||
| < 0.2020| :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a vulnerability, we would like to know about it so we can take steps to address it as quickly as possible.
|
||||
|
||||
### Report Format
|
||||
|
||||
When reporting vulnerabilities, please include the following details:
|
||||
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce the issue
|
||||
- Potential impact if left unaddressed
|
||||
- Suggested mitigation or resolution if any
|
||||
|
||||
### Response Time
|
||||
|
||||
We aim to confirm the receipt of your vulnerability report within 48 hours. Depending on the severity and complexity of the issue, we strive to investigate the issue and provide an initial response within a week.
|
||||
|
||||
### Disclosure Policy
|
||||
|
||||
If the vulnerability is confirmed, we will work on a fix and plan a release. We ask that you do not publicly disclose the issue until it has been addressed by us.
|
||||
|
||||
## Security Practices
|
||||
|
||||
We follow industry-standard security practices, including regular audits of the services and features we provide, to maintain the trust of our users.
|
||||
|
||||
## Security Updates
|
||||
|
||||
We will communicate any security updates through our standard communication channels, including our project's release notes and official website.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We greatly value the work of security researchers and believe that responsible disclosure of vulnerabilities is a valuable contribution to the security of the Internet. We encourage users to contribute to the security of our project by reporting any security-related issues to us.
|
14
app.json
14
app.json
@ -1,8 +1,18 @@
|
||||
{
|
||||
"name": "gun-server",
|
||||
"website": "http://gun.js.org",
|
||||
"website": "http://gun.eco/",
|
||||
"repository": "https://github.com/amark/gun",
|
||||
"logo": "https://avatars3.githubusercontent.com/u/8811914",
|
||||
"keywords": ["node", "gun", "gunDB", "database","graph","offline-first"],
|
||||
"description": "Javascript, Offline-First Javascript Graph Database Server Peer"
|
||||
"description": "Javascript, Offline-First Javascript Graph Database Server Peer",
|
||||
"env": {
|
||||
"NPM_CONFIG_PRODUCTION": {
|
||||
"description": "If you do not want default features, set to \"true\".",
|
||||
"value": "false"
|
||||
},
|
||||
"PEERS": {
|
||||
"description": "Comma-separated list of peer urls to connect to",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
as.js
110
as.js
@ -1,7 +1,73 @@
|
||||
;(function(){
|
||||
function as(el, gun, cb){
|
||||
function as(el, gun, cb, opt){
|
||||
el = $(el);
|
||||
if(gun === as.gui && as.el && as.el.is(el)){ return }
|
||||
|
||||
opt = opt || {};
|
||||
opt.match = opt.match || '{{ ';
|
||||
opt.end = opt.end || ' }}';
|
||||
;(function(){ // experimental
|
||||
function nest(t, s,e, r, i,tmp,u){
|
||||
if(r && !r.length){ return t||'' }
|
||||
if(!t){ return [] }
|
||||
e = e || s;
|
||||
i = t.indexOf(s, i||0);
|
||||
if(0 > i){ return [] }
|
||||
tmp = t.indexOf(e, i+1);
|
||||
if(!r){ return [t.slice(i+s.length, tmp)].concat(nest(t, s,e, r, tmp,tmp,u)) }
|
||||
return t.slice(0,i)+r[0]+nest(t.slice(tmp+e.length), s,e, r.slice(1), 0,tmp,u);
|
||||
}
|
||||
|
||||
/* experimental */
|
||||
function template(tag, attr){
|
||||
var html = (tag = $(tag))[0].outerHTML, sub, tmp;
|
||||
if(html && (0 > html.indexOf(opt.match))){ return }
|
||||
if(!attr){
|
||||
$.each(tag[0].attributes, function(i,v){
|
||||
if(!v){ return }
|
||||
if(!nest(v.value, opt.match, opt.end).length){ return }
|
||||
template(tag, v.name)
|
||||
});
|
||||
if((sub = tag.children()).length){
|
||||
return sub.each(function(){ template(this) });
|
||||
}
|
||||
}
|
||||
var data = [], plate = attr? tag.attr(attr) : tag.html();
|
||||
tmp = nest(plate, opt.match, opt.end);
|
||||
if(!tmp.length){ return }
|
||||
$.each(tmp, function(pos, match){
|
||||
var expr = match.split(' ');
|
||||
var path = (expr[0]).split('.');
|
||||
if(expr = expr.slice(1).join(' ')){
|
||||
expr = new Function("_", "b", "return (_)" + expr);
|
||||
}
|
||||
var val = (expr && expr('')) || '';
|
||||
data.push(val);
|
||||
if(!attr){ tag.text(val) }
|
||||
|
||||
var ref = gun, sup = [], tmp;
|
||||
if(tmp = tag.attr('name')){ sup.push(tmp) }
|
||||
tag.parents("[name]").each(function(){
|
||||
sup.push($(this).attr('name'));
|
||||
});
|
||||
$.each(path = sup.reverse().concat(path), function(i,v){
|
||||
ref = ref.get(v);
|
||||
});
|
||||
ref.on(function(v){
|
||||
v = data[pos] = expr? expr(v) : v;
|
||||
var tmp = nest(plate, opt.match, opt.end, data);
|
||||
if(attr){
|
||||
tag.attr(attr, tmp);
|
||||
} else {
|
||||
tag.text(tmp);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
template(el);
|
||||
|
||||
}());
|
||||
|
||||
as.gui = gun;
|
||||
as.el = el;
|
||||
if(el.data('as')){
|
||||
@ -59,10 +125,12 @@
|
||||
if(many && ui.is('.sort')){
|
||||
var up = ui.closest("[name='#']");
|
||||
var tmp = as.sort(data, up.parent().children().last());
|
||||
up.insertAfter(tmp);
|
||||
tmp? up.insertAfter(tmp) : up.prependTo(up.parent());
|
||||
}
|
||||
if(as.lock === gui){ return }
|
||||
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
|
||||
if(!(data && data instanceof Object)){
|
||||
(ui[0] && u === ui[0].value)? ui.text(data) : ui.val(data);
|
||||
}
|
||||
ui.data('was', data);
|
||||
if(cb){
|
||||
cb(data, key, ui);
|
||||
@ -80,12 +148,7 @@
|
||||
}, wait || 200);
|
||||
}
|
||||
}
|
||||
as.sort = function sort(id, li){
|
||||
var num = parseFloat(id);
|
||||
var id = $(li).find('.sort').text() || -Infinity;
|
||||
var at = num >= parseFloat(id);
|
||||
return at ? li : sort(id, li.prev());
|
||||
}
|
||||
as.sort = function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
|
||||
$(document).on('keyup', 'input, textarea, [contenteditable]', as.wait(function(){
|
||||
var el = $(this);
|
||||
var data = (el[0] && u === el[0].value)? el.text() : el.val();
|
||||
@ -94,7 +157,7 @@
|
||||
as.lock = g;
|
||||
g.put(data);
|
||||
}, 99));
|
||||
$(document).on('submit', 'form', function(e){ e.preventDefault() });
|
||||
//$(document).on('submit', 'form', function(e){ e.preventDefault() });
|
||||
var u;
|
||||
window.as = as;
|
||||
$.as = as;
|
||||
@ -146,4 +209,31 @@
|
||||
;$(function(){
|
||||
$('.page').not(':first').hide();
|
||||
$.as.route(location.hash.slice(1));
|
||||
$(JOY.start = JOY.start || function(){ $.as(document, gun, null, JOY.opt) });
|
||||
|
||||
if($('body').attr('peers')){ (console.warn || console.log)('Warning: Please upgrade <body peers=""> to https://github.com/eraeco/joydb#peers !') }
|
||||
|
||||
});
|
||||
;(function(){ // need to isolate into separate module!
|
||||
var joy = window.JOY = function(){};
|
||||
joy.auth = function(a,b,cb,o){
|
||||
if(!o){ o = cb ; cb = 0 }
|
||||
if(o === true){
|
||||
gun.user().create(a, b);
|
||||
return;
|
||||
}
|
||||
gun.user().auth(a,b, cb,o);
|
||||
}
|
||||
|
||||
var opt = joy.opt = window.CONFIG || {}, peers;
|
||||
$('link[type=peer]').each(function(){ (peers || (peers = [])).push($(this).attr('href')) });
|
||||
!window.gun && (opt.peers = opt.peers || peers || (function(){
|
||||
(console.warn || console.log)('Warning: No peer provided, defaulting to DEMO peer. Do not run in production, or your data will be regularly wiped, reset, or deleted. For more info, check https://github.com/eraeco/joydb#peers !');
|
||||
return ['https://gunjs.herokuapp.com/gun'];
|
||||
}()));
|
||||
window.gun = window.gun || Gun(opt);
|
||||
|
||||
gun.on('auth', function(ack){
|
||||
console.log("Your namespace is publicly available at", ack.soul);
|
||||
});
|
||||
}());
|
188
axe.js
188
axe.js
@ -1,99 +1,109 @@
|
||||
;(function(){
|
||||
|
||||
/* UNBUILD */
|
||||
var root;
|
||||
if(typeof window !== "undefined"){ root = window }
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg, req){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
/* UNBUILD */
|
||||
var sT = setTimeout || {}, u;
|
||||
if(typeof window !== ''+u){ sT.window = window }
|
||||
var AXE = (sT.window||'').AXE || function(){};
|
||||
if(AXE.window = sT.window){ AXE.window.AXE = AXE }
|
||||
|
||||
;USE(function(module){
|
||||
if(typeof window !== "undefined"){ module.window = window }
|
||||
var tmp = module.window || module;
|
||||
var AXE = tmp.AXE || function(){};
|
||||
var Gun = (AXE.window||'').GUN || require('./gun');
|
||||
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
|
||||
|
||||
if(AXE.window = module.window){ try{
|
||||
AXE.window.AXE = AXE;
|
||||
tmp = document.createEvent('CustomEvent');
|
||||
tmp.initCustomEvent('extension', false, false, {type: "AXE"});
|
||||
(window.dispatchEvent || window.fireEvent)(tmp);
|
||||
window.postMessage({type: "AXE"}, '*');
|
||||
} catch(e){} }
|
||||
//if(!Gun.window){ try{ require('./lib/axe') }catch(e){} }
|
||||
if(!Gun.window){ require('./lib/axe') }
|
||||
|
||||
try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
|
||||
module.exports = AXE;
|
||||
})(USE, './root');
|
||||
|
||||
;USE(function(module){
|
||||
Gun.on('opt', function(at){ start(at) ; this.to.next(at) }); // make sure to call the "next" middleware adapter.
|
||||
|
||||
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
|
||||
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
|
||||
function start(root){
|
||||
if(root.axe){ return }
|
||||
var opt = root.opt, peers = opt.peers;
|
||||
if(false === opt.axe){ return }
|
||||
if(!Gun.window){ return } // handled by ^ lib/axe.js
|
||||
var w = Gun.window, lS = w.localStorage || opt.localStorage || {}, loc = w.location || opt.location || {}, nav = w.navigator || opt.navigator || {};
|
||||
var axe = root.axe = {}, tmp, id;
|
||||
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); // DAM!
|
||||
|
||||
Gun.on('opt', function(at){
|
||||
if(!at.axe){
|
||||
at.axe = {};
|
||||
var p = at.opt.peers, tmp;
|
||||
// 1. If any remembered peers or from last cache or extension
|
||||
// 2. Fallback to use hard coded peers from dApp
|
||||
// 3. Or any offered peers.
|
||||
//if(Gun.obj.empty(p)){
|
||||
// Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
|
||||
// p[url] = {url: url, axe: {}};
|
||||
// });
|
||||
//}
|
||||
// Our current hypothesis is that it is most optimal
|
||||
// to take peers in a common network, and align
|
||||
// them in a line, where you only have left and right
|
||||
// peers, so messages propagate left and right in
|
||||
// a linear manner with reduced overlap, and
|
||||
// with one common superpeer (with ready failovers)
|
||||
// in case the p2p linear latency is high.
|
||||
// Or there could be plenty of other better options.
|
||||
console.log("axe", at.opt);
|
||||
if(at.opt.super){
|
||||
function verify(msg, send, at) {
|
||||
var peers = Object.keys(p), puts = Object.keys(msg.put), i, j, peer;
|
||||
var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
|
||||
for (i=0; i < peers.length; ++i) {
|
||||
peer = p[peers[i]];
|
||||
//if (peer.url) {console.log('AXE do not reject superpeers'); send(msg, peer); continue;} /// always send to superpeers?
|
||||
if (!peer.id) {console.log('AXE peer without id: ', peer); continue;}
|
||||
if (!Gun.subscribe[soul] || !Gun.subscribe[soul][peer.id]) { console.log('AXE SAY reject msg to peer: %s, soul: %s', peer.id, soul); continue; }
|
||||
send(msg, peer);
|
||||
}
|
||||
}
|
||||
AXE.say = function(msg, send, at) {
|
||||
if (!msg.put) { send(msg); return; }
|
||||
console.log('AXE HOOK!! ', msg);
|
||||
verify(msg, send, at);
|
||||
};
|
||||
/// TODO: remove peer from all Gun.subscribe. On `mesh.bye` event?
|
||||
}
|
||||
if(at.opt.super){
|
||||
at.on('in', USE('./lib/super', 1), at);
|
||||
} else {
|
||||
//at.on('in', input, at);
|
||||
}
|
||||
}
|
||||
this.to.next(at); // make sure to call the "next" middleware adapter.
|
||||
});
|
||||
tmp = peers[id = loc.origin + '/gun'] = peers[id] || {};
|
||||
tmp.id = tmp.url = id; tmp.retry = tmp.retry || 0;
|
||||
tmp = peers[id = 'http://localhost:8765/gun'] = peers[id] || {};
|
||||
tmp.id = tmp.url = id; tmp.retry = tmp.retry || 0;
|
||||
Gun.log.once("AXE", "AXE enabled: Trying to find network via (1) local peer (2) last used peers (3) a URL parameter, and last (4) hard coded peers.");
|
||||
Gun.log.once("AXEWarn", "Warning: AXE is in alpha, use only for testing!");
|
||||
var last = lS.peers || ''; if(last){ last += ' ' }
|
||||
last += ((loc.search||'').split('peers=')[1]||'').split('&')[0];
|
||||
|
||||
function input(msg){
|
||||
var at = this.as, to = this.to;
|
||||
}
|
||||
root.on('bye', function(peer){
|
||||
this.to.next(peer);
|
||||
if(!peer.url){ return } // ignore WebRTC disconnects for now.
|
||||
if(!nav.onLine){ peer.retry = 1 }
|
||||
if(peer.retry){ return }
|
||||
if(axe.fall){ delete axe.fall[peer.url || peer.id] }
|
||||
(function next(){
|
||||
if(!axe.fall){ setTimeout(next, 9); return } // not found yet
|
||||
var fall = Object.keys(axe.fall||''), one = fall[(Math.random()*fall.length) >> 0];
|
||||
if(!fall.length){ lS.peers = ''; one = 'https://gunjs.herokuapp.com/gun' } // out of peers
|
||||
if(peers[one]){ next(); return } // already choose
|
||||
mesh.hi(one);
|
||||
}());
|
||||
});
|
||||
|
||||
module.exports = AXE;
|
||||
})(USE, './axe');
|
||||
root.on('hi', function(peer){ // TEMPORARY! Try to connect all peers.
|
||||
this.to.next(peer);
|
||||
if(!peer.url){ return } // ignore WebRTC disconnects for now.
|
||||
return; // DO NOT COMMIT THIS FEATURE YET! KEEP TESTING NETWORK PERFORMANCE FIRST!
|
||||
(function next(){
|
||||
if(!peer.wire){ return }
|
||||
if(!axe.fall){ setTimeout(next, 9); return } // not found yet
|
||||
var one = (next.fall = next.fall || Object.keys(axe.fall||'')).pop();
|
||||
if(!one){ return }
|
||||
setTimeout(next, 99);
|
||||
mesh.say({dam: 'opt', opt: {peers: one}}, peer);
|
||||
}());
|
||||
});
|
||||
|
||||
}());
|
||||
function found(text){
|
||||
|
||||
axe.fall = {};
|
||||
((text||'').match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ig)||[]).forEach(function(url){
|
||||
axe.fall[url] = {url: url, id: url, retry: 0}; // RETRY
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
// TODO: Finish porting below? Maybe not.
|
||||
|
||||
Object.keys(last.peers||'').forEach(function(key){
|
||||
tmp = peers[id = key] = peers[id] || {};
|
||||
tmp.id = tmp.url = id;
|
||||
});
|
||||
tmp = peers[id = 'https://guntest.herokuapp.com/gun'] = peers[id] || {};
|
||||
tmp.id = tmp.url = id;
|
||||
|
||||
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); // DAM!
|
||||
mesh.way = function(msg){
|
||||
if(root.$ === msg.$ || (msg._||'').via){
|
||||
mesh.say(msg, opt.peers);
|
||||
return;
|
||||
}
|
||||
var at = (msg.$||'')._;
|
||||
if(!at){ mesh.say(msg, opt.peers); return }
|
||||
if(msg.get){
|
||||
if(at.axe){ return } // don't ask for it again!
|
||||
at.axe = {};
|
||||
}
|
||||
mesh.say(msg, opt.peers);
|
||||
}
|
||||
}
|
||||
|
||||
if(last){ found(last); return }
|
||||
try{ fetch(((loc.search||'').split('axe=')[1]||'').split('&')[0] || loc.axe || 'https://raw.githubusercontent.com/wiki/amark/gun/volunteer.dht.md').then(function(res){
|
||||
return res.text()
|
||||
}).then(function(text){
|
||||
found(lS.peers = text);
|
||||
}).catch(function(){
|
||||
found(); // nothing
|
||||
})}catch(e){found()}
|
||||
}
|
||||
|
||||
var empty = {}, yes = true;
|
||||
try{ if(typeof module != ''+u){ module.exports = AXE } }catch(e){}
|
||||
}());
|
4
browser.js
Normal file
4
browser.js
Normal file
@ -0,0 +1,4 @@
|
||||
// if(!(typeof navigator == "undefined") && navigator.product == "ReactNative"){
|
||||
// require("./lib/mobile.js");
|
||||
// }
|
||||
module.exports = require('./gun.js');
|
217
examples/Main.js
Normal file
217
examples/Main.js
Normal file
@ -0,0 +1,217 @@
|
||||
import { render } from './iris/js/lib/preact.js';
|
||||
import { Router, route } from './iris/js/lib/preact-router.es.js';
|
||||
import { createHashHistory } from './iris/js/lib/history.production.min.js';
|
||||
import { Component } from './iris/js/lib/preact.js';
|
||||
import { Link } from './iris/js/lib/preact.match.js';
|
||||
|
||||
import Helpers from './iris/js/Helpers.js';
|
||||
import { html } from './iris/js/Helpers.js';
|
||||
import QRScanner from './iris/js/QRScanner.js';
|
||||
import PeerManager from './iris/js/PeerManager.js';
|
||||
import Session from './iris/js/Session.js';
|
||||
import { translate as t } from './iris/js/Translation.js';
|
||||
|
||||
import Settings from './iris/js/views/Settings.js';
|
||||
import LogoutConfirmation from './iris/js/views/LogoutConfirmation.js';
|
||||
import Chat from './iris/js/views/Chat.js';
|
||||
import Store from './iris/js/views/Store.js';
|
||||
import Checkout from './iris/js/views/Checkout.js';
|
||||
import Product from './iris/js/views/Product.js';
|
||||
import Login from './iris/js/views/Login.js';
|
||||
import Profile from './iris/js/views/Profile.js';
|
||||
import Group from './iris/js/views/Group.js';
|
||||
import Message from './iris/js/views/Message.js';
|
||||
import Follows from './iris/js/views/Follows.js';
|
||||
import Feed from './iris/js/views/Feed.js';
|
||||
import About from './iris/js/views/About.js';
|
||||
import Explorer from './iris/js/views/Explorer.js';
|
||||
import Contacts from './iris/js/views/Contacts.js';
|
||||
import Torrent from './iris/js/views/Torrent.js';
|
||||
|
||||
import VideoCall from './iris/js/components/VideoCall.js';
|
||||
import Identicon from './iris/js/components/Identicon.js';
|
||||
import MediaPlayer from './iris/js/components/MediaPlayer.js';
|
||||
import Footer from './iris/js/components/Footer.js';
|
||||
import State from './iris/js/State.js';
|
||||
import Icons from './iris/js/Icons.js';
|
||||
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
const isElectron = (userAgent.indexOf(' electron/') > -1);
|
||||
if (!isElectron && ('serviceWorker' in navigator)) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('iris/serviceworker.js')
|
||||
.catch(function(err) {
|
||||
// registration failed :(
|
||||
console.log('ServiceWorker registration failed: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
State.init();
|
||||
Session.init({autologin: true});
|
||||
PeerManager.init();
|
||||
|
||||
Helpers.checkColorScheme();
|
||||
|
||||
const APPLICATIONS = [ // TODO: move editable shortcuts to State.local gun
|
||||
{url: '/', text: t('home'), icon: Icons.home},
|
||||
{url: '/feed', text: t('feed'), icon: Icons.feed},
|
||||
{url: '/media', text: t('media'), icon: Icons.play},
|
||||
{url: '/settings', text: t('settings'), icon: Icons.settings},
|
||||
{url: '/store', text: t('store'), icon: Icons.store},
|
||||
{url: '/explorer', text: t('explorer'), icon: Icons.folder},
|
||||
{url: '/chat', text: t('messages'), icon: Icons.chat},
|
||||
// {url: '/store', text: t('store'), icon: Icons.store}, // restore when it works!
|
||||
{},
|
||||
{url: '../stats.html', text: 'Gun node stats'},
|
||||
{url: '../iris/index.html', text: 'Iris', icon: html`<img src="iris/img/icon128.png" width=24/>`},
|
||||
{url: '../infinite-scroll/index.html', text: 'Infinite scroll'},
|
||||
{url: '../chat/index.html', text: 'Chat'},
|
||||
{url: '../game/space.html', text: 'Space'},
|
||||
{},
|
||||
{url: 'https://gun.eco/docs/', text: 'Gun documentation'},
|
||||
{url: 'https://examples.iris.to/components/', text: 'Iris web components'}
|
||||
];
|
||||
|
||||
const HomeView = () => {
|
||||
return html`
|
||||
<div class="main-view">
|
||||
<div class="centered-container public-messages-view">
|
||||
<h1>Hello, world!</h1>
|
||||
<p>Here you can find sample applications and utilities for <a href="https://github.com/amark/gun">GUN</a>.</p>
|
||||
<p>If you need any help, please feel free to join the GUN community chat: <a href="http://chat.gun.eco">http://chat.gun.eco</a></p>
|
||||
<a href="/explorer" class="msg"><div class="msg-content">
|
||||
<b>Explorer</b>
|
||||
<p>Explore the data saved on the GUN database. Open to the side while using an application and see the data change in real-time.</p>
|
||||
</div></a>
|
||||
<a class="msg" href="game/space.html"><div class="msg-content">
|
||||
<div class="img-container"><img src="iris/img/space-game.jpg"/></div>
|
||||
<b>Space</b>
|
||||
<p>Spaceflight game. Open in 2 or more browser windows.</p>
|
||||
</div></a>
|
||||
<a class="msg" href="/iris/index.html"><div class="msg-content">
|
||||
<div class="img-container"><img src="iris/img/screenshot.png"/></div>
|
||||
<b>Iris</b>
|
||||
<p>Decentralized Twitter/Instagram. Provides modular components that can be reused in other applications (including this one).</p>
|
||||
</div></a>
|
||||
<a native class="msg" href="/chat/index.html"><div class="msg-content">
|
||||
<div class="img-container"><img src="iris/img/gun-chat.jpg"/></div>
|
||||
<b>Chat</b>
|
||||
<p>Shoutbox!</p>
|
||||
</div></a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
class MenuView extends Component {
|
||||
componentDidMount() {
|
||||
State.local.get('showMenu').on(showMenu => this.setState({showMenu}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const pub = Session.getPubKey();
|
||||
return html`
|
||||
<div class="application-list ${this.state.showMenu ? 'menu-visible-xs' : ''}">
|
||||
<a href="/profile/${pub}">
|
||||
<span class="icon"><${Identicon} str=${pub} width=40/></span>
|
||||
<span class="text" style="font-size: 1.2em;border:0;margin-left: 7px;"><iris-text user="${pub}" path="profile/name" editable="false"/></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
${APPLICATIONS.map(a => {
|
||||
if (a.url) {
|
||||
return html`
|
||||
<a href=${a.url}>
|
||||
<span class="icon">${a.icon || Icons.circle}</span>
|
||||
<span class="text">${a.text}</span>
|
||||
</a>`;
|
||||
} else {
|
||||
return html`<br/><br/>`;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
class Main extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
State.local.get('loggedIn').on(loggedIn => this.setState({loggedIn}));
|
||||
}
|
||||
|
||||
handleRoute(e) {
|
||||
let activeRoute = e.url;
|
||||
if (!activeRoute && window.location.hash) {
|
||||
return route(window.location.hash.replace('#', '')); // bubblegum fix back navigation
|
||||
}
|
||||
document.title = 'Iris';
|
||||
if (activeRoute && activeRoute.length > 1) { document.title += ' - ' + Helpers.capitalize(activeRoute.replace('/', '')); }
|
||||
State.local.get('activeRoute').put(activeRoute);
|
||||
QRScanner.cleanupScanner();
|
||||
}
|
||||
|
||||
onClickOverlay() {
|
||||
if (this.state.showMenu) {
|
||||
this.setState({showMenu: false});
|
||||
}
|
||||
}
|
||||
|
||||
toggleMenu(show) {
|
||||
this.setState({showMenu: typeof show === 'undefined' ? !this.state.showMenu : show});
|
||||
}
|
||||
|
||||
render() {
|
||||
const content = this.state.loggedIn ? html`
|
||||
<div class="visible-xs-flex" style="border-bottom:var(--sidebar-border-right)">
|
||||
<svg onClick=${() => State.local.get('showMenu').put(this.showMenu = !this.showMenu)} style="padding: 5px;cursor:pointer;" viewBox="0 -53 384 384" width="40px"><path d="m368 154.667969h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/><path d="m368 32h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/><path d="m368 277.332031h-352c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h352c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0"/></svg>
|
||||
</div>
|
||||
<section class="main" style="flex-direction: row;">
|
||||
<${MenuView}/>
|
||||
<div style="flex: 3; display: flex">
|
||||
<${Router} history=${createHashHistory()} onChange=${e => this.handleRoute(e)}>
|
||||
<${HomeView} path="/"/>
|
||||
<${Feed} path="/feed"/>
|
||||
<${Feed} path="/search/:term?/:type?"/>
|
||||
<${Feed} path="/media" index="media"/>
|
||||
<${Login} path="/login"/>
|
||||
<${Chat} path="/chat/:id?"/>
|
||||
<${Message} path="/post/:hash"/>
|
||||
<${Torrent} path="/torrent/:id"/>
|
||||
<${About} path="/about"/>
|
||||
<${Settings} path="/settings"/>
|
||||
<${LogoutConfirmation} path="/logout"/>
|
||||
<${Profile} path="/profile/:id?" tab="profile"/>
|
||||
<${Profile} path="/replies/:id?" tab="replies"/>
|
||||
<${Profile} path="/likes/:id?" tab="likes"/>
|
||||
<${Profile} path="/media/:id" tab="media"/>
|
||||
<${Group} path="/group/:id?"/>
|
||||
<${Store} path="/store/:store?"/>
|
||||
<${Checkout} path="/checkout/:store?"/>
|
||||
<${Product} path="/product/:product/:store"/>
|
||||
<${Product} path="/product/new" store=Session.getPubKey()/>
|
||||
<${Explorer} path="/explorer/:node"/>
|
||||
<${Explorer} path="/explorer"/>
|
||||
<${Follows} path="/follows/:id"/>
|
||||
<${Follows} followers=${true} path="/followers/:id"/>
|
||||
<${Contacts} path="/contacts"/>
|
||||
</${Router}>
|
||||
</div>
|
||||
</section>
|
||||
<${VideoCall}/>
|
||||
` : '';
|
||||
return html`
|
||||
<div id="main-content">
|
||||
${content}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
render(html`<${Main}/>`, document.body);
|
||||
|
||||
$('body').css('opacity', 1); // use opacity because setting focus on display: none elements fails
|
@ -22,7 +22,7 @@
|
||||
"@angular/router": "^4.1.0",
|
||||
"core-js": "^2.4.1",
|
||||
"express-http-proxy": "^1.0.1",
|
||||
"gun": "^0.7.4",
|
||||
"gun": "https://github.com/amark/gun.git#master",
|
||||
"ngx-pipes": "^2.0.5",
|
||||
"rxjs": "^5.3.0",
|
||||
"underscore": "^1.8.3",
|
10047
examples/angular/package-lock.json
generated
10047
examples/angular/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@ import { pick } from 'underscore';
|
||||
export function on$(node, cleanup = true): Observable<any> {
|
||||
return Observable.fromEventPattern(
|
||||
h => {
|
||||
// there is no way to off() an on() until at least one value is trigerred
|
||||
// there is no way to off() an on() until at least one value is triggered
|
||||
// so that we can access the event listener to off() it
|
||||
const signal = { stop: false };
|
||||
node.on((data, key, at, ev) => {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,11 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3 id="pid"></h3>
|
||||
<script src="../gun.js"></script>
|
||||
<!-- <script src="../axe.js"></script> -->
|
||||
<script src="../axe.js"></script>
|
||||
<script src="../lib/radix.js"></script>
|
||||
<script src="../lib/webrtc.js"></script>
|
||||
<!-- <script src="../sea.js"></script> -->
|
||||
<script>
|
||||
var pid = location.hash.slice(1);
|
||||
@ -23,14 +26,17 @@
|
||||
Gun.on('opt', function(ctx) {
|
||||
this.to.next(ctx);
|
||||
ctx.on('hi', function(opt) {
|
||||
console.log('HI!! PEER', new Date(), opt.pid);
|
||||
// console.log('HI!! PEER', new Date(), opt.pid);
|
||||
setTimeout(function() {
|
||||
document.getElementById('pid').innerHTML = gun._.opt.pid;
|
||||
});
|
||||
});
|
||||
if (pid) {
|
||||
ctx.on('out', function(msg) {
|
||||
msg.pid = pid;
|
||||
this.to.next(msg);
|
||||
});
|
||||
}
|
||||
// if (pid) {
|
||||
// ctx.on('out', function(msg) {
|
||||
// msg.pid = pid;
|
||||
// this.to.next(msg);
|
||||
// });
|
||||
// }
|
||||
});
|
||||
|
||||
var gun = Gun(opt);
|
||||
|
21
examples/basic/chat.html
Normal file
21
examples/basic/chat.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<ul id='list'></ul>
|
||||
<form id='form'>
|
||||
<input id='who' placeholder='name'>
|
||||
<input id='what' placeholder='say'>
|
||||
<input type='submit' value='send'>
|
||||
</form>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/axe.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/emojione@4.0.0/lib/js/emojione.min.js"></script>
|
||||
<script>
|
||||
gun = GUN(), chat = gun.get("note" + location.hash.replace('#','/')), view = document;
|
||||
form.onsubmit = (eve) => { chat.set(who.value+': '+what.value), eve.preventDefault(what.value = "") }
|
||||
chat.map().on(function show(data, id){
|
||||
(view.line = view.getElementById(id) || view.createElement("li")).id = id;
|
||||
list.appendChild(view.line).innerText = emojione.shortnameToUnicode(data);
|
||||
window.scroll(0, list.offsetHeight);
|
||||
(list.beep = new SpeechSynthesisUtterance()).text = "new";
|
||||
list.beep.rate = 10, list.beep.pitch = 2, window.speechSynthesis.speak(list.beep);
|
||||
});
|
||||
</script>
|
2
examples/basic/emoji.html
Normal file
2
examples/basic/emoji.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<p>Moved to <a href="./chat.html">./chat.html</a>!</p>
|
38
examples/basic/meet.html
Normal file
38
examples/basic/meet.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<center>must press play or unmute on new videos to accept meeting</center>
|
||||
<center id="videos">
|
||||
<video id="me" width="100%" controls autoplay playsinline muted></video>
|
||||
</center>
|
||||
<center>Stream <select id="select"><option id="from">from</option></select></center>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
<script src="../../../gun/lib/webrtc.js"></script>
|
||||
|
||||
<script>;(async function(){
|
||||
streams = {}, gun = Gun(location.origin + '/gun'); //gun = GUN();
|
||||
mesh = gun.back('opt.mesh');
|
||||
|
||||
(await (me.stream = navigator.mediaDevices).enumerateDevices()).forEach((device,i) => {
|
||||
if('videoinput' !== device.kind){ return }
|
||||
var opt = $(from).clone().prependTo('select').get(0);
|
||||
$(opt).text(opt.id = device.label || 'Camera '+i);
|
||||
opt.value = device.deviceId;
|
||||
});
|
||||
|
||||
$('select').on('change', async eve => { $(from).text('Off'); // update label
|
||||
if('Off' == select.value){ return me.srcObject.getTracks()[0].stop() }
|
||||
mesh.hi(me.srcObject = await me.stream.getUserMedia({ audio: true,
|
||||
video: (select.value && {deviceId: {exact: select.value}}) || {facingMode: "environment"}
|
||||
}));
|
||||
});
|
||||
|
||||
gun.on('rtc', async function(eve){ var ui, src;
|
||||
console.log("?RTC?", eve.peer && eve.peer.connectionState, eve);
|
||||
if(!(src = eve.streams)){ return }
|
||||
ui = $('#v'+(src=src[0]).id).get(0) || $(me).clone().attr('id', 'v'+src.id).prependTo('#videos').get(0); // reuse or create video element
|
||||
ui.srcObject = src;
|
||||
});
|
||||
}());</script>
|
9
examples/basic/note.html
Normal file
9
examples/basic/note.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<style>html, body, textarea { width: 100%; height: 100%; padding: 0; margin: 0; }</style>
|
||||
<textarea id="view" placeholder="write here..."></textarea>
|
||||
<script src="../../../gun/gun.js"></script><script>
|
||||
gun = GUN(location.origin + '/gun');
|
||||
note = gun.get('note').get(location.hash.replace('#','')||1);
|
||||
view.oninput = () => { note.put(view.value) };
|
||||
note.on((data) => { view.value = data });
|
||||
</script>
|
2
examples/basic/paste.html
Normal file
2
examples/basic/paste.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<p>Moved to <a href="./note.html">./note.html</a>!</p>
|
148
examples/basic/poll.html
Normal file
148
examples/basic/poll.html
Normal file
@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
<!-- script src="../../../gun/axe.js"></script -->
|
||||
<script> // main init!
|
||||
var app = {
|
||||
view: $, // replace with not jquery!
|
||||
data: GUN('http://localhost:8765/gun'), // peer-to-peer database!
|
||||
};
|
||||
app.user = app.data.user().recall({sessionStorage: true});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="login" class="center pad">
|
||||
<style>
|
||||
#login input {
|
||||
max-width: 6em;
|
||||
}
|
||||
</style>
|
||||
<form id="sign" onsubmit="app.login(event)">
|
||||
<input id="alias" placeholder="username" class="jot rim">
|
||||
<input id="pass" type="password" placeholder="passphrase" class="jot rim">
|
||||
<input id="in" type="submit" value="sign in" class="green whitet act gap sap rim">
|
||||
<input id="up" type="button" value="sign up" onclick="app.register()" class="act gap sap rim">
|
||||
</form>
|
||||
<script>
|
||||
app.login = function(eve){
|
||||
if(app.error(eve)){ return }
|
||||
app.data.user().auth(
|
||||
app.view('#alias').val(),
|
||||
app.view('#pass').val(),
|
||||
app.error
|
||||
);
|
||||
};
|
||||
|
||||
app.register = function(eve){
|
||||
app.data.user().create(
|
||||
app.view('#alias').val(),
|
||||
app.view('#pass').val(),
|
||||
app.login
|
||||
);
|
||||
};
|
||||
|
||||
app.data.on('auth', function(eve){
|
||||
app.view('#sign').hide(); // hide login form upon logging in.
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="poll" class="pad">
|
||||
<style>
|
||||
#poll {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#poll div {
|
||||
margin: 1%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(window.onhashchange = async function(){
|
||||
app.poll = app.data.get(location.hash.slice(1));
|
||||
app.poll.map().on(function(data, id){
|
||||
app.render(id = 'p'+String.hash(id), '.q', '#poll', data).css({order: data.how}).data('as',{$:this});
|
||||
console.log("poll?", id, data);
|
||||
});
|
||||
})();
|
||||
app.render = function(id, model, onto, data){
|
||||
var ui = $(
|
||||
$('#'+id).get(0) ||
|
||||
$('.model').find(model).clone(true).attr('id', id).appendTo(onto)
|
||||
);
|
||||
$.each(data, function(field, val){
|
||||
if($.isPlainObject(val)){ return }
|
||||
ui.find("[name='" + field + "']").val(val).text(val);
|
||||
});
|
||||
return ui;
|
||||
}
|
||||
</script>
|
||||
<div class="model">
|
||||
<div class="q">
|
||||
<span name="what"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="make" class="pad">
|
||||
<style>
|
||||
#make #add {
|
||||
border-radius: 100%;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
line-height: 0em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<button id="add" onclick="app.add()" class="green whitet act">+</button>
|
||||
<span class="hint">add new title, text, question...</span>
|
||||
<script>
|
||||
app.add = async function(){
|
||||
if(app.error(app.user)){ return }
|
||||
var tmp = await (app.poll = app.poll || app.data.get(location.hash.slice(1)));
|
||||
if(!tmp){ app.poll = app.user.get('poll').set({}) }
|
||||
app.poll.set({how: tmp = Object.keys(tmp||'').length || 1, what: "Question " + tmp });
|
||||
if(!location.hash){ location.hash = (await app.poll)._['#'] }
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<span id="error">
|
||||
<span id="err"></span>
|
||||
<script>
|
||||
app.error = function(eve){
|
||||
app.view('#err').text('').hide();
|
||||
if(!eve){ return }
|
||||
if(eve.preventDefault){
|
||||
eve.preventDefault();
|
||||
return;
|
||||
}
|
||||
if(eve._ && !eve.is){ eve = {err: "Not signed in!"} }
|
||||
if(!eve.err){ return }
|
||||
app.view('#err').text(eve.err).show();
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
</span>
|
||||
|
||||
<style>
|
||||
#error { position: fixed; top: 0; width: 100%; text-align: center; background: white; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="../style.css"/>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=Oxygen');
|
||||
html, body { font-family: "Oxygen", sans-serif; }
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
45
examples/basic/post.html
Normal file
45
examples/basic/post.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<h1>Posts</h1>
|
||||
|
||||
<form id="sign">
|
||||
<input id="alias" placeholder="username">
|
||||
<input id="pass" type="password" placeholder="passphrase">
|
||||
<input id="in" type="submit" value="sign in">
|
||||
<input id="up" type="button" value="sign up">
|
||||
</form>
|
||||
|
||||
<form id="said">
|
||||
<input id="say" placeholder="write here...">
|
||||
<input id="speak" type="submit" value="say">
|
||||
</form>
|
||||
|
||||
<ul></ul>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
<script src="../../../gun/axe.js"></script>
|
||||
|
||||
<script>
|
||||
gun = GUN(), user = gun.user().recall({sessionStorage: true});
|
||||
|
||||
$('#sign').on('submit', login);
|
||||
$('#up').on('click', () => { user.create($('#alias').val(), $('#pass').val(), login) });
|
||||
function login(eve){
|
||||
eve.preventDefault();
|
||||
user.auth($('#alias').val(), $('#pass').val());
|
||||
};
|
||||
|
||||
gun.on('auth', () => { $('#sign').hide(), user.get('said').map().on(show) });
|
||||
function show(data, id){
|
||||
return ($('#' + id).get(0) || $('<li>').attr('id', id).prependTo('ul')).text(data);
|
||||
};
|
||||
|
||||
$('#said').on('submit', (eve) => {
|
||||
eve.preventDefault();
|
||||
if(!user.is){ return }
|
||||
user.get('said').set($('#say').val());
|
||||
$('#say').val("");
|
||||
});
|
||||
</script>
|
@ -1,4 +1,4 @@
|
||||
<html><body>
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
html, body {
|
||||
background: rgb(245, 245, 245);
|
||||
@ -70,13 +70,98 @@
|
||||
Public Key: <input id="pub">
|
||||
</div></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
//var gun = Gun();
|
||||
var gun = Gun('http://localhost:8080/gun');
|
||||
|
||||
// extend SEA functions to base64 encode encrypted data
|
||||
// workaround for https://github.com/amark/gun/issues/783
|
||||
|
||||
(() => {
|
||||
const _encrypt = SEA.encrypt;
|
||||
SEA.encrypt = function(...args) {
|
||||
return _encrypt.apply(this, args).then(enc => btoa(JSON.stringify(enc)));
|
||||
}
|
||||
|
||||
const _decrypt = SEA.decrypt;
|
||||
SEA.decrypt = function(data, ...args) {
|
||||
try { data = JSON.parse(atob(data)); }
|
||||
finally { return _decrypt.apply(this, [data, ...args]); }
|
||||
}
|
||||
})();
|
||||
|
||||
// override User functions to fix several issues
|
||||
// see https://github.com/amark/gun/issues/808
|
||||
|
||||
SEA.Gun.User.prototype.grant = function grant(to, cb) {
|
||||
const gun = this; const user = gun.back(-1).user();
|
||||
const pair = user._.sea; let path = '';
|
||||
|
||||
gun.back(at => { if (at.has) { path += at.get; } });
|
||||
|
||||
(async () => {
|
||||
let enc, sec;
|
||||
|
||||
if (sec = await user.get('trust').get(pair.pub).get(path).then()) {
|
||||
sec = await SEA.decrypt(sec, pair);
|
||||
|
||||
} else {
|
||||
sec = SEA.random(24).toString();
|
||||
enc = await SEA.encrypt(sec, pair);
|
||||
|
||||
user.get('trust').get(pair.pub).get(path).put(enc);
|
||||
}
|
||||
|
||||
let pub = to.get('pub') .then();
|
||||
let epub = to.get('epub').then();
|
||||
|
||||
pub = await pub; epub = await epub;
|
||||
|
||||
const dh = await SEA.secret (epub, pair);
|
||||
enc = await SEA.encrypt(sec, dh);
|
||||
|
||||
// if pub is not already in trust, first put an empty node
|
||||
// workaround for https://github.com/amark/gun/issues/844
|
||||
|
||||
if (!await user.get('trust').get(pub).then()) {
|
||||
await user.get('trust').get(pub).get(path).put({}).then();
|
||||
}
|
||||
|
||||
user.get('trust').get(pub).get(path).put(enc, cb);
|
||||
})();
|
||||
|
||||
return gun;
|
||||
}
|
||||
|
||||
SEA.Gun.User.prototype.secret = function(data, cb) {
|
||||
const gun = this; const user = gun.back(-1).user();
|
||||
const pair = user._.sea; let path = '';
|
||||
|
||||
gun.back(at => { if (at.has) { path += at.get; } });
|
||||
|
||||
(async () => {
|
||||
let enc, sec;
|
||||
|
||||
if (sec = await user.get('trust').get(pair.pub).get(path).then()) {
|
||||
sec = await SEA.decrypt(sec, pair);
|
||||
|
||||
} else {
|
||||
sec = SEA.random(24).toString();
|
||||
enc = await SEA.encrypt(sec, pair);
|
||||
|
||||
user.get('trust').get(pair.pub).get(path).put(enc);
|
||||
}
|
||||
|
||||
enc = await SEA.encrypt(data, sec);
|
||||
gun.put(enc, cb);
|
||||
})();
|
||||
|
||||
return gun;
|
||||
}
|
||||
|
||||
var gun = Gun('http://localhost:8765/gun');
|
||||
var user = gun.user();
|
||||
var LI = {};
|
||||
|
||||
@ -94,7 +179,7 @@ $('#sign').on('submit', function(e){
|
||||
gun.on('auth', function(){
|
||||
$('#sign').hide();
|
||||
$('#profile').show();
|
||||
var pub = user.pair().pub;
|
||||
var pub = user._.sea.pub;
|
||||
$('#pub').val(pub);
|
||||
return;
|
||||
$("#search").val(pub).trigger('blur');
|
||||
@ -126,15 +211,25 @@ $('#search').on('blur', function(e){
|
||||
ev.off();
|
||||
return;
|
||||
}
|
||||
Gun.node.is(data, async function(v, k){
|
||||
if(k === LI.busy){ return }
|
||||
var key = await find.get('trust').get(user.pair().pub).get(k+'profile').then();
|
||||
var mix = await Gun.SEA.secret(await find.get('epub').then(), user.pair());
|
||||
key = await Gun.SEA.decrypt(key, mix);
|
||||
var val = await Gun.SEA.decrypt(v, key);
|
||||
$('#'+k).val(val || v);
|
||||
|
||||
Gun.node.is(data, async (enc, id) => {
|
||||
if (id === LI.busy) { return; }
|
||||
|
||||
const pair = user._.sea;
|
||||
let key, val;
|
||||
|
||||
if (key =
|
||||
await find.get('trust').get(pair.pub).get(id + 'profile').then()) {
|
||||
const mix = await Gun.SEA.secret(await find.get('epub').then(), pair);
|
||||
|
||||
key = await Gun.SEA.decrypt(key, mix);
|
||||
val = await Gun.SEA.decrypt(enc, key);
|
||||
|
||||
// decode encrypted data to show 'SEA{...}'
|
||||
} else { val = JSON.parse(atob(enc)); }
|
||||
|
||||
$('#' + id).val(val);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
||||
</script>
|
121
examples/basic/schedule.html
Normal file
121
examples/basic/schedule.html
Normal file
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1><button id="left">←</button> <span id="date"></span> Schedule <button id="right">→</button></h1>
|
||||
|
||||
<form id="add">
|
||||
<style>input[type="number"]{ width: 4em; }</style>
|
||||
<input id="what" placeholder="What?">
|
||||
<input id="where" placeholder="Where?">
|
||||
<input type="number" id="hour"><script>hour.value = new Date().getHours() % 12 || 12</script> :
|
||||
<input type="number" id="min" value="0">
|
||||
<select id="ampm">
|
||||
<option value="">am</option>
|
||||
<option value="1">pm</option>
|
||||
<script>ampm.children[new Date().getHours() < 12? 0 : 1].selected='selected'</script>
|
||||
</select>
|
||||
<input id="id" type="hidden">
|
||||
<input id="go" type="submit" value="add">
|
||||
<div id="err"></div>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.none { display: none; }
|
||||
p, ul, li { list-style-type: none; margin: 0; padding: 0; }
|
||||
</style>
|
||||
|
||||
<ul></ul>
|
||||
|
||||
<div class="model none">
|
||||
<li>
|
||||
<b class="when"></b>
|
||||
<span class="what"></span>
|
||||
<u class="where"></u>
|
||||
<span class="sort none">0</span>
|
||||
<button class="edit"><</button>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/nts.js"></script>
|
||||
<script src="../../../gun/lib/webrtc.js"></script>
|
||||
|
||||
<script>
|
||||
var name = 'schedule/' + location.hash.slice(1);
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
//var gun = Gun('http://localhost:8765/gun');
|
||||
//var gun = Gun();
|
||||
|
||||
$('#add').on('submit', function(event){
|
||||
event.preventDefault();
|
||||
event = {};
|
||||
if(!schedule.on){ return err.innerText = "No date!" }
|
||||
event.when = new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate(), hour.value % 12 + (ampm.value? 12 : 0), min.value).getTime();
|
||||
if(!(event.what = what.value)){ return err.innerText = "No description!" }
|
||||
if(!(event.where = where.value)){ return err.innerText = "No location!" }
|
||||
var day = gun.get(name+now(event.when));
|
||||
day.get(id.value || String.random(9)).put(event);
|
||||
what.value = where.value = id.value = err.innerText = '';
|
||||
go.value = 'add';
|
||||
schedule(event.when);
|
||||
});
|
||||
|
||||
function schedule(ms){
|
||||
var day = new Date(ms);
|
||||
if(schedule.on && schedule.on.toLocaleDateString() === day.toLocaleDateString()){ return } schedule.on = day;
|
||||
$('#date').text(day.getFullYear()+' '+ day.toString().split(' ')[1] +' '+day.getDate());
|
||||
day = gun.get(name+now(ms));
|
||||
$('ul').empty();
|
||||
day.map().on(UI);
|
||||
}
|
||||
schedule(+new Date());
|
||||
|
||||
$('#left').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() - 1)) });
|
||||
$('#right').on('click', function(){ schedule(+new Date(schedule.on.getFullYear(), schedule.on.getMonth(), schedule.on.getDate() + 1)) });
|
||||
|
||||
function UI(event, id){
|
||||
if(!event){ return }
|
||||
var when = new Date(event.when);
|
||||
if(schedule.on && when.toLocaleDateString() !== schedule.on.toLocaleDateString()){ return }
|
||||
var ul = $('ul')
|
||||
var li = $("#cal-" + id)[0]; // grab if exists
|
||||
if(!li){
|
||||
li = $('.model li').clone(true) // else create it
|
||||
.attr('id', 'cal-' + id);
|
||||
}
|
||||
li = (UI.last = sort(event.when, ul.children('li').last())[0])? $(li).insertAfter(UI.last) : $(li).prependTo(ul);
|
||||
li.find('.what').text(event.what);
|
||||
li.find('.where').text(event.where);
|
||||
li.find('.sort').text(event.when);
|
||||
li.find('.edit').val(id);
|
||||
|
||||
var time = when.toLocaleTimeString();
|
||||
li.find('.when').text(time.split(':').slice(0,2).join(':') + time.slice(-2));
|
||||
};
|
||||
|
||||
$(document).on('click', '.edit', function(){
|
||||
go.value = 'update';
|
||||
id.value = this.value;
|
||||
what.value = $(this).parent().find('.what').text();
|
||||
where.value = $(this).parent().find('.where').text();
|
||||
var when = new Date(parseFloat($(this).parent().find('.sort').text()));
|
||||
hour.value = when.getHours() % 12 || 12;
|
||||
min.value = when.getMinutes();
|
||||
ampm.value = when.getHours() < 12? '' : 1;
|
||||
what.focus();
|
||||
});
|
||||
|
||||
function now(t){
|
||||
return new Date(t || Gun.state()).toLocaleDateString().split('/').reverse().join('/')
|
||||
}
|
||||
|
||||
function sort(num, li){ return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity)? li : sort(num, li.prev()) }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
59
examples/basic/screen.html
Normal file
59
examples/basic/screen.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<video id="video" width="100%"></video>
|
||||
<center>
|
||||
<button id="record">Record</button>
|
||||
<button id="play">Play</button>
|
||||
</center>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
|
||||
<script>
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
var record = {recorder: null, recording: false};
|
||||
|
||||
$('#record').on('click', ()=>{
|
||||
if(!record.ing){ return record.stream() }
|
||||
$('#record').text("Record");
|
||||
if(record.ing.stop){ record.ing.stop() }
|
||||
record.ing = false;
|
||||
})
|
||||
|
||||
record.stream = function(){
|
||||
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
|
||||
var chunks = []; // we have a stream, we can record it
|
||||
record.ing = new MediaRecorder(stream);
|
||||
record.ing.ondataavailable = eve => chunks.push(eve.data);
|
||||
record.ing.onstop = eve => record.save(new Blob(chunks));
|
||||
record.ing.start()
|
||||
$('#record').text("End");
|
||||
}, err => { console.log(err) });
|
||||
}
|
||||
|
||||
record.save = function(data){
|
||||
record.file = record.file || new FileReader();
|
||||
record.file.readAsDataURL(data);
|
||||
record.file.onloadend = function(){
|
||||
var b64 = record.file.result;
|
||||
b64 = "data:video/webm" + b64.slice(b64.indexOf(';'));
|
||||
gun.get('test').get('screen').put(b64);
|
||||
}
|
||||
}
|
||||
|
||||
$('#play').on('click', ()=>{
|
||||
if(record.playing){
|
||||
$('#play').text("Play")
|
||||
$('#video').get(0).stop();
|
||||
record.playing = false;
|
||||
return;
|
||||
}
|
||||
$('#play').text("Stop");
|
||||
record.playing = true;
|
||||
gun.get('test').get('screen').once((data)=>{
|
||||
if(!data){ return }
|
||||
$('#video').get(0).src = data;
|
||||
$('#video').get(0).play()
|
||||
})
|
||||
})
|
||||
</script>
|
@ -1,50 +0,0 @@
|
||||
<h1>Search</h1>
|
||||
|
||||
<form id="ask">
|
||||
<input id="search" placeholder="search..." autocomplete="off">
|
||||
</form>
|
||||
|
||||
<div id="answer"></div>
|
||||
<ul></ul>
|
||||
|
||||
<small>Note: No data is indexed by default, you need to add some!</small>
|
||||
|
||||
<script src="../../examples/jquery.js"></script>
|
||||
<script src="../../gun.js"></script>
|
||||
<script src="../../sea.js"></script>
|
||||
<script src="../../lib/space.js"></script>
|
||||
|
||||
<script>
|
||||
var gun = Gun();
|
||||
var ask = {};
|
||||
|
||||
$('#search').on('keyup', function(e){
|
||||
ask.now = (this.value||'').toLowerCase().replace(/[\W_]+/g,"");
|
||||
if(ask.last === ask.now){ return }
|
||||
ask.last = ask.now;
|
||||
clearTimeout(ask.to);
|
||||
ask.to = setTimeout(search, 20);
|
||||
});
|
||||
|
||||
function search(){
|
||||
var key = ask.now;
|
||||
gun.get('Q').space(key, function(ack){
|
||||
if(!ack || key !== ask.now){ return }
|
||||
UI(ack)
|
||||
});
|
||||
}
|
||||
|
||||
function UI(ack){
|
||||
$('#answer').text(ack.data || '');
|
||||
var $ul = $('ul').empty(), tree = ack.tree;
|
||||
Gun.obj.map(tree, function(v,k){
|
||||
$('<li>').text(k +' - ' + v).appendTo($ul);
|
||||
});
|
||||
};
|
||||
|
||||
function load(DATA){
|
||||
Gun.obj.map(DATA, function(v,k){
|
||||
gun.get('Q').space(k, v);
|
||||
});
|
||||
}
|
||||
</script>
|
55
examples/basic/stream.html
Normal file
55
examples/basic/stream.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<center>
|
||||
<img id="img" width="100%"><br/>
|
||||
Stream <select id="select"><option id="from">from</option></select>
|
||||
add <input id="pass" placeholder="password" type="password">
|
||||
resolution <input id="res" value="240" step="32" max="1080" type="number" style="width:3em;">
|
||||
or <input id="upload" type="file">
|
||||
</center>
|
||||
<video id="video" width="100%" controls autoplay style="display: none;"></video>
|
||||
<canvas id="canvas" width="0" style="display: none;"></canvas>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
<script src="../../../gun/lib/webrtc.js"></script>
|
||||
|
||||
<script>;(async function(){
|
||||
gun = Gun(location.origin + '/gun'); //gun = GUN();
|
||||
|
||||
stream = canvas.getContext('2d'), stream.from = navigator.mediaDevices;
|
||||
|
||||
(await stream.from.enumerateDevices()).forEach((device,i) => {
|
||||
if('videoinput' !== device.kind){ return }
|
||||
var opt = $(from).clone().prependTo('select').get(0);
|
||||
$(opt).text(opt.id = device.label || 'Camera '+i);
|
||||
opt.value = device.deviceId;
|
||||
});
|
||||
|
||||
$('select').on('change', async eve => { $(from).text('Off'); // update label
|
||||
if('Off' == select.value){ return video.srcObject.getTracks()[0].stop() }
|
||||
video.srcObject = await stream.from.getUserMedia({ audio: false,
|
||||
video: (select.value && {deviceId: {exact: select.value}}) || {facingMode: "environment"}
|
||||
});
|
||||
});
|
||||
$('#upload').on('change', async eve => { console.log("Check ./upload.html") })
|
||||
|
||||
setInterval(async tmp => {
|
||||
if(!(video.srcObject||'').active){ return }
|
||||
var size = parseInt(res.value);
|
||||
stream.drawImage(video, 0,0,
|
||||
canvas.width = size || video.videoWidth * 0.1,
|
||||
canvas.height = (size * (video.videoHeight/video.videoWidth)) || video.videoHeight * 0.1
|
||||
);
|
||||
var b64 = canvas.toDataURL('image/jpeg');
|
||||
if(pass.value){ b64 = await SEA.encrypt(b64, pass.value) }
|
||||
gun.get('test').get('video').put(b64);
|
||||
}, 99);
|
||||
|
||||
gun.get('test').get('video').on(async data => {
|
||||
if(pass.value){ data = await SEA.decrypt(data, pass.value) }
|
||||
img.src = data; // Beware: Some browsers memory leak fast src updates.
|
||||
});
|
||||
|
||||
}());</script>
|
@ -1,3 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<h1>Tables</h1>
|
||||
|
||||
<form id="sign">
|
||||
|
35
examples/basic/upload.html
Normal file
35
examples/basic/upload.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<div class="model" style="display: none;">
|
||||
<video width="100%" controls autoplay></video>
|
||||
<audio width="100%" controls autoplay></audio>
|
||||
<img style="max-width: 100%;">
|
||||
</div>
|
||||
<center>
|
||||
<p>Drag & drop videos, songs, or images! <input id="upload" type="file" multiple></p>
|
||||
</center>
|
||||
|
||||
<script src="../../../gun/lib/yson.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/lib/dom.js"></script>
|
||||
<script src="../../../gun/lib/upload.js"></script>
|
||||
|
||||
<script>
|
||||
gun = GUN(location.origin + '/gun');
|
||||
|
||||
$('html, #upload').upload(function resize(eve, up){
|
||||
if(up){ return up.shrink(eve, resize, 1024) }
|
||||
var b64 = (eve.base64 || ((eve.event || eve).target || eve).result || eve); // which one? try all!
|
||||
gun.get('test').get((eve.id+(new Date).getUTCSeconds()) % 60).put(b64); // limit uploads to 1 of 60 slots.
|
||||
});
|
||||
|
||||
gun.get('test').map().once(function(data){
|
||||
if("string" != typeof data){ return }
|
||||
var type = data.split(';')[0], ui;
|
||||
if(type.indexOf('image') + 1){ ui = $("img").get(0) }
|
||||
if(type.indexOf('video') + 1){ ui = $('video').get(0) }
|
||||
if(type.indexOf('audio') + 1){ ui = $('audio').get(0) }
|
||||
if(!ui){ return }
|
||||
$(ui).clone().prependTo('center').get(0).src = data;
|
||||
});
|
||||
</script>
|
@ -1,52 +1,2 @@
|
||||
<h1>User</h1>
|
||||
|
||||
<form id="sign">
|
||||
<input id="alias" placeholder="username">
|
||||
<input id="pass" type="password" placeholder="passphrase">
|
||||
<input id="in" type="submit" value="sign in">
|
||||
<input id="up" type="button" value="sign up">
|
||||
<input id="mask" type="button" value="MetaMask Login">
|
||||
</form>
|
||||
|
||||
<ul></ul>
|
||||
|
||||
<form id="said">
|
||||
<input id="say">
|
||||
<input id="speak" type="submit" value="speak">
|
||||
</form>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
|
||||
var user = gun.user().recall({sessionStorage: true});
|
||||
|
||||
$('#up').on('click', function(e){
|
||||
user.create($('#alias').val(), $('#pass').val(), login);
|
||||
});
|
||||
function login(e){
|
||||
user.auth($('#alias').val(), $('#pass').val());
|
||||
return false; // e.preventDefault();
|
||||
};
|
||||
$('#sign').on('submit', login);
|
||||
$('#mask').on('click', login);
|
||||
|
||||
gun.on('auth', function(){
|
||||
$('#sign').hide();
|
||||
user.get('said').map().on(UI);
|
||||
});
|
||||
|
||||
$('#said').on('submit', function(e){
|
||||
e.preventDefault();
|
||||
//if(!user.is){ return }
|
||||
user.get('said').set($('#say').val());
|
||||
$('#say').val("");
|
||||
});
|
||||
|
||||
function UI(say, id){
|
||||
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
|
||||
$(li).text(say);
|
||||
};
|
||||
</script>
|
||||
<!DOCTYPE html>
|
||||
<p>Moved to <a href="./post.html">./post.html</a>!</p>
|
62
examples/basic/video.html
Normal file
62
examples/basic/video.html
Normal file
@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<video id="video" width="100%" controls autoplay></video>
|
||||
<center>
|
||||
<input id="pass" placeholder="password">
|
||||
Record <button class="record">Camera</button> or <button class="record">Screen</button>
|
||||
</center>
|
||||
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
|
||||
gun.get('test').get('video').on(async function(data){
|
||||
if($('#pass').val()){ data = await SEA.decrypt(data, $('#pass').val()) }
|
||||
$('#video').get(0).src = data;
|
||||
})
|
||||
|
||||
$('.record').on('click', function(){
|
||||
if(record.ing){
|
||||
if(record.ing.stop){ record.ing.stop() }
|
||||
$(this).text(record.type);
|
||||
record.ing = false;
|
||||
return;
|
||||
}
|
||||
record(record.type = $(this).text());
|
||||
$(this).text("End");
|
||||
})
|
||||
|
||||
function record(type){
|
||||
if('Camera' === type){
|
||||
navigator.getMedia({ video: true, audio: true }, load, error);
|
||||
}
|
||||
if('Screen' === type){
|
||||
navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then(load, error);
|
||||
}
|
||||
function load(media){
|
||||
var chunks = [];
|
||||
record.ing = new MediaRecorder(media);
|
||||
record.ing.ondataavailable = function(eve){ chunks.push(eve.data) }
|
||||
record.ing.onstop = function(eve){record.save(new Blob(chunks)) }
|
||||
record.ing.start();
|
||||
}
|
||||
function error(err){ console.log(err) }
|
||||
}
|
||||
|
||||
record.save = function(data){
|
||||
record.file = record.file || new FileReader();
|
||||
record.file.readAsDataURL(data);
|
||||
record.file.onloadend = async function(){
|
||||
var b64 = record.file.result, pass;
|
||||
b64 = $('#video').get(0).src = "data:video/webm" + b64.slice(b64.indexOf(';'));
|
||||
if($('#pass').val()){ b64 = await SEA.encrypt(b64, $('#pass').val()) }
|
||||
gun.get('test').get('video').put(b64);
|
||||
}
|
||||
}
|
||||
|
||||
navigator.getMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia
|
||||
|| navigator.mozGetUserMedia || navigator.msGetUserMedia);
|
||||
</script>
|
@ -1,161 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Converse</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
#converse {
|
||||
font-size: 16pt;
|
||||
}
|
||||
#converse .box {
|
||||
margin-bottom: 0.2em;
|
||||
padding: 1em;
|
||||
border-radius: 0.1em;
|
||||
}
|
||||
#converse b:after {
|
||||
content: " ";
|
||||
}
|
||||
#converse li .when {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2em;
|
||||
padding: 0.5em 1em;
|
||||
background: rgba(100%,100%,100%,0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
#converse li:hover .when {
|
||||
opacity: 1;
|
||||
right: 0em;
|
||||
}
|
||||
.poiret {
|
||||
font-family: 'Poiret One', sans-serif;
|
||||
}
|
||||
.large {
|
||||
font-size: 200%;
|
||||
}
|
||||
#converse .what, #converse .who {
|
||||
cursor: text;
|
||||
outline: none;
|
||||
display: inline;
|
||||
min-width: 1em;
|
||||
padding-left: 1px;
|
||||
}
|
||||
[contentEditable=true]:empty:not(:focus):before{
|
||||
content:attr(data-text)
|
||||
}
|
||||
|
||||
#title {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
<head>
|
||||
<title>Converse</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Poiret+One' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
.chat__heading {
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.say {
|
||||
margin: 0 0 0.4em 0.4em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
.chat__form-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#name-input {
|
||||
margin-top: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.chat__form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 50px;
|
||||
background-color: white;
|
||||
border: 2px solid white;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#message-input {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="converse" class="hue2 page">
|
||||
<div class="pad">
|
||||
<div id='title' class="poiret large rubric whitet">Have a Conversation...</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li class="none"></li>
|
||||
</ul>
|
||||
.chat__name-input {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
<form class="white huet2 box">
|
||||
<div>
|
||||
<div class="say hue2 right whitet box act">say</div>
|
||||
<b id="name-input" class="jot left who" contenteditable="true" data-text="Name"></b>
|
||||
<p id="message-input" class="jot left what" contenteditable="true" data-text="Write a message..."></p>
|
||||
</div>
|
||||
</form>
|
||||
.chat__message-input {
|
||||
flex: 5;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
<div class="model">
|
||||
<li class="white huet2 box">
|
||||
<b class="who"></b>
|
||||
<p class="what"></p>
|
||||
<span class="sort none">0</span>
|
||||
<div class="when"></div>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
.chat__submit {
|
||||
padding: 10px;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="/gun/nts.js"></script>
|
||||
<script>
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
var chat = gun.get('converse/' + location.hash.slice(1));
|
||||
.chat__submit:hover::after {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
$("form .say").on('click', submit);
|
||||
$("form .what").on('keydown', enter);
|
||||
function enter(e){
|
||||
if(e.which !== 13){ return }
|
||||
submit(e);
|
||||
}
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
.chat__submit:focus::after {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
var msg = {when: Gun.time.is()};
|
||||
.chat__submit::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.3s;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
msg.who = $('form .who').text();
|
||||
if(!msg.who){
|
||||
msg.who = 'user' + Gun.text.random(3);
|
||||
$('form .who').text(msg.who);
|
||||
}
|
||||
.chat__message-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 60px 20px;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
msg.what = $('form .what').text();
|
||||
if(!msg.what){ return }
|
||||
.chat__message {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
chat.set(msg);
|
||||
$('form .what').text('');
|
||||
}
|
||||
.chat__name {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
chat.map().val(function(msg, id){
|
||||
if(!msg){ return }
|
||||
var ul = $('ul');
|
||||
var last = sort(msg.when, ul.children('li').last());
|
||||
.chat__when {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2em;
|
||||
padding: 10px;
|
||||
background: rgba(100%, 100%, 100%, 0.9);
|
||||
opacity: 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
var li = $("#msg-" + id)[0]; // grab if exists
|
||||
if(!li){
|
||||
li = $('.model li').clone(true) // else create it
|
||||
.attr('id', 'msg-' + id)
|
||||
.insertAfter(last);
|
||||
}
|
||||
.chat__message:hover .chat__when {
|
||||
opacity: 1;
|
||||
right: 0em;
|
||||
}
|
||||
|
||||
// bind the message data into the UI
|
||||
li = $(li);
|
||||
li.find('.who').text(msg.who);
|
||||
li.find('.what').text(msg.what);
|
||||
li.find('.sort').text(msg.when);
|
||||
@media (max-width: 567px) {
|
||||
.chat__heading {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
var time = new Date(msg.when);
|
||||
li.find('.when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
|
||||
<body>
|
||||
<div class="chat hue2 page">
|
||||
<h2 id='title' class="chat__heading hue2 whitet">Have a Conversation...</h2>
|
||||
<ul class="chat__message-list">
|
||||
<li class="none"></li>
|
||||
</ul>
|
||||
|
||||
$('html, body').stop(true, true)
|
||||
.animate({scrollTop: ul.height()});
|
||||
});
|
||||
<div class="chat__form-container hue2">
|
||||
<form class="chat__form">
|
||||
<label for="name-input" class="visually-hidden">Name</label>
|
||||
<input id="name-input" class="chat__name-input" placeholder="Name"></input>
|
||||
<label for="message-input" class="visually-hidden">Message</label>
|
||||
<input id="message-input" class="chat__message-input" placeholder="Write a message..."></input>
|
||||
<button class="chat__submit say hue2">say</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
function sort(id, li){
|
||||
var num = parseFloat(id);
|
||||
var id = $(li).find('.sort').text() || -Infinity;
|
||||
var at = num >= parseFloat(id);
|
||||
return at ? li : sort(id, li.prev());
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<div class="model">
|
||||
<li class="chat__message white huet2 box">
|
||||
<b class="chat__name"></b>
|
||||
<p class="chat__message-text"></p>
|
||||
<span class="sort none">0</span>
|
||||
<div class="chat__when"></div>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="/gun/nts.js"></script>
|
||||
<script>
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
var chat = gun.get('converse/' + location.hash.slice(1));
|
||||
|
||||
$(".chat__submit").on('click', submit);
|
||||
$(".chat_form").on('keydown', enter);
|
||||
function enter(e) {
|
||||
if (e.which !== 13) { return }
|
||||
submit(e);
|
||||
}
|
||||
function submit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var msg = { when: Gun.state() };
|
||||
|
||||
msg.who = $('.chat__name-input').val();
|
||||
if (!msg.who) {
|
||||
msg.who = 'user' + String.random(3);
|
||||
$('.chat__name-input').val(msg.who);
|
||||
}
|
||||
|
||||
msg.what = $('.chat__message-input').val();
|
||||
if (!msg.what) { return }
|
||||
|
||||
chat.set(msg);
|
||||
$('.chat__message-input').val('').focus();
|
||||
}
|
||||
|
||||
chat.map().once(function (msg, id) {
|
||||
if (!msg) { return }
|
||||
var messageList = $('.chat__message-list');
|
||||
var last = sort(msg.when, messageList.children('li').last());
|
||||
|
||||
var li = $("#msg-" + id)[0]; // grab if exists
|
||||
if (!li) {
|
||||
li = $('.model li').clone(true) // else create it
|
||||
.attr('id', 'msg-' + id)
|
||||
.insertAfter(last);
|
||||
}
|
||||
|
||||
// bind the message data into the UI
|
||||
li = $(li);
|
||||
li.find('.chat__name').text(msg.who);
|
||||
li.find('.chat__message-text').text(msg.what);
|
||||
li.find('.sort').text(msg.when);
|
||||
|
||||
var time = new Date(msg.when);
|
||||
li.find('.chat__when').text(time.toDateString() + ', ' + time.toLocaleTimeString());
|
||||
|
||||
$('html, body').stop(true, true)
|
||||
.animate({ scrollTop: messageList.height() });
|
||||
});
|
||||
|
||||
function sort(num, li) { return parseFloat(num) >= parseFloat($(li).find('.sort').text() || -Infinity) ? li : sort(num, li.prev()) }
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
379
examples/docs.html
Normal file
379
examples/docs.html
Normal file
@ -0,0 +1,379 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- always start with these two lines to set a clean baseline for different devices -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src="jquery.js"></script>
|
||||
|
||||
<title>Docs</title>
|
||||
</head>
|
||||
<body class="black whitet">
|
||||
<style>
|
||||
/*
|
||||
Choose white text on a black background so you can add color in.
|
||||
Pick your favorite font and choose a font size.
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Oxygen');
|
||||
html, body {
|
||||
font-family: "Oxygen", sans-serif;
|
||||
}
|
||||
|
||||
[contenteditable]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.meta-on, div:hover, ul:hover, ol:hover, li:hover, p:hover, span:hover, form:hover, button:hover, input:hover, textarea:hover, img:hover {
|
||||
outline: 1px solid;
|
||||
animation: meta-on 3s infinite;
|
||||
transition: none !important;
|
||||
} @keyframes meta-on {
|
||||
0% {outline-color: magenta;}
|
||||
33% {outline-color: cyan;}
|
||||
66% {outline-color: yellow;}
|
||||
100% {outline-color: magenta;}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hold full hue2">
|
||||
<div id="page" class="max focus gap" style="margin-top: 9%;"></div>
|
||||
</div>
|
||||
|
||||
<script src="../../gun/gun.js"></script>
|
||||
<script src="../../gun/lib/monotype.js"></script>
|
||||
<script src="../../gun/lib/meta.js"></script>
|
||||
<script src="../../gun/lib/normalize.js"></script>
|
||||
<script async src="../../gun/lib/fun.js"></script>
|
||||
|
||||
<script async src="../../gun/lib/wave.js"></script>
|
||||
<!-- script async src="https://edide.io/music.lib"></script -->
|
||||
|
||||
<script>
|
||||
var gun = Gun();
|
||||
var page = {};
|
||||
//var gun = Gun(['https://guntest.herokuapp.com/gun', 'http://localhost:8765/gun']);
|
||||
|
||||
;(window.onhashchange = function(){
|
||||
var file = (location.hash||'').slice(1);
|
||||
var S = +new Date;
|
||||
$('#page').empty().attr('contenteditable', 'false');
|
||||
gun.get('test/gun/docs/'+file).get('what').map().on(function render(data, i, msg, eve){
|
||||
var tmp = page[i] || '';
|
||||
var last = Gun.state.is(gun._.root.graph[msg.put['#']], i);
|
||||
if(last < tmp.last){ return }
|
||||
//});
|
||||
//if(window.LOCK){ return }
|
||||
var p = $('#page').children().get(i);
|
||||
if(!p){
|
||||
$('#page').append('<p>');
|
||||
setTimeout(function(){ render(data, i, msg, eve) },0);
|
||||
return;
|
||||
}
|
||||
var DBG = {s: +new Date};
|
||||
var r = monotype(p);
|
||||
DBG.mono = +new Date;
|
||||
var safe = $.normalize(data);
|
||||
DBG.norm = +new Date;
|
||||
p.outerHTML = data;
|
||||
DBG.html = +new Date;
|
||||
r.restore();
|
||||
DBG.rest = +new Date;
|
||||
//console.log("mono:", DBG.mono - DBG.s, "norm:", DBG.norm - DBG.mono, 'html:', DBG.html - DBG.norm, 'rest:', DBG.rest - DBG.html, ':::', msg, eve);
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
|
||||
window.requestAnimationFrame(function frame(){
|
||||
window.requestAnimationFrame(frame, 16);
|
||||
|
||||
}, 16);
|
||||
|
||||
document.execCommand('defaultParagraphSeparator', false, 'p');
|
||||
meta.edit({
|
||||
name: "Edit",
|
||||
combo: ['E'],
|
||||
use: function(eve){
|
||||
console.log('on');
|
||||
}, on: function(eve){
|
||||
if($(eve.target).closest('p').length){ return }
|
||||
var edit = this;
|
||||
setTimeout(function(){ meta.flip(false) },1);
|
||||
edit.init();
|
||||
$(document).on('keydown.tmp', '[contenteditable]', function(eve){
|
||||
if(eve.which != 13){ return }
|
||||
eve.preventDefault();
|
||||
var r = window.getSelection().getRangeAt(0);
|
||||
var c = r.commonAncestorContainer, p;
|
||||
r.deleteContents();
|
||||
var p = c.splitText? $(c.splitText(r.startOffset)).parent() : $(c);
|
||||
var n = $("<"+p.get(0).tagName+">"), f;
|
||||
p.contents().each(function(){
|
||||
if(this === c){ return f = true }
|
||||
if(!f){ return }
|
||||
n.append(this);
|
||||
});
|
||||
p.after(n);
|
||||
edit.select(n.get(0));
|
||||
// make sure we re-save & sync each changed paragraph.
|
||||
edit.save(p);
|
||||
p.nextAll().each(function(){
|
||||
edit.save(this);
|
||||
});
|
||||
}).on('keyup.tmp', '[contenteditable]', function(eve){
|
||||
//$('#debug').val(doc.html());
|
||||
var p = $(window.getSelection().anchorNode).closest('p'), tmp;
|
||||
(tmp = page[p.index()] || (page[p.index()] = {})).last = (+new Date) + 99;
|
||||
clearTimeout(tmp.to); tmp.to = setTimeout(function(){
|
||||
var DBG = {s: +new Date};
|
||||
var r = monotype(p);
|
||||
DBG.m = +new Date;
|
||||
var html = p.html() || '';
|
||||
DBG.g = +new Date;
|
||||
if(!html && !p.prev().length && !p.next().length && !$('#page').html()){
|
||||
edit.init();
|
||||
}
|
||||
DBG.i = +new Date;
|
||||
var safe = $.normalize(html);
|
||||
DBG.n = +new Date;
|
||||
p.html(safe);
|
||||
DBG.h = +new Date;
|
||||
r.restore();
|
||||
DBG.r = +new Date;
|
||||
edit.save(p);
|
||||
DBG.p = +new Date;
|
||||
//console.log("save:", DBG.p - DBG.r, "rest:", DBG.r - DBG.h, "html:", DBG.h - DBG.n, "norm:", DBG.n - DBG.i, 'init:', DBG.i - DBG.g, 'grab:', DBG.g - DBG.m, 'mono:', DBG.m - DBG.s);
|
||||
},50)});
|
||||
},
|
||||
up: function(){
|
||||
console.log("UP");
|
||||
$('[contenteditable=true]').off('.tmp');
|
||||
},
|
||||
init: function(){
|
||||
var edit = this;
|
||||
var doc = $('#page').attr('contenteditable', 'true');
|
||||
if(!doc.text()){
|
||||
doc.html('<p class="loud crack"></p>');
|
||||
}
|
||||
edit.select(doc.children().first().get(0));
|
||||
},
|
||||
save: function(p){
|
||||
p = $(p);
|
||||
var i = p.index();// = Array.prototype.indexOf.call(parent.children, child);
|
||||
var file = (location.hash||'').slice(1);
|
||||
var data = (p.get(0)||{}).outerHTML||'';
|
||||
//data = $.normalize(data); // GOOD TO DO SECURITY ON SENDING SIDE TOO!!!
|
||||
window.LOCK = true;
|
||||
gun.get('test/gun/docs/'+file).get('what').get(i).put(data);
|
||||
window.LOCK = false;
|
||||
},
|
||||
select: function(p){
|
||||
var s = window.getSelection(),
|
||||
r = document.createRange();
|
||||
if(p.innerHTML){
|
||||
r.setStart(p, 0);
|
||||
r.collapse(true);
|
||||
s.removeAllRanges();
|
||||
s.addRange(r);
|
||||
return;
|
||||
}
|
||||
p.innerHTML = '\u00a0';
|
||||
r.selectNodeContents(p);
|
||||
s.removeAllRanges();
|
||||
s.addRange(r);
|
||||
document.execCommand('delete', false, null);
|
||||
}
|
||||
});
|
||||
|
||||
;(function(){
|
||||
|
||||
meta.edit({name: "Design", combo: ['D']});
|
||||
meta.edit({name: "Fill", combo: ['D','F'], // TODO!
|
||||
use: function(eve){},
|
||||
on: function(eve){
|
||||
var on = meta.tap();
|
||||
meta.ask('Color name, code, or URL?', function(color){
|
||||
on.css('background', color);
|
||||
}, true);
|
||||
},
|
||||
up: function(eve){}
|
||||
});
|
||||
|
||||
meta.edit({name: "Add", combo: ['D','A']});
|
||||
meta.edit({name: "Row", combo: ['D','A', 'R'],
|
||||
on: function(eve){
|
||||
meta.tap().append('<div style="min-height: 9em; padding: 2%;">');
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Columns", combo: ['D','A','C'],
|
||||
on: function(eve){
|
||||
var on = meta.tap().addClass('center'), tmp, c;
|
||||
var html = '<div class="unit col" style="min-height: 9em; padding: 2%;"></div>';
|
||||
if(!on.children('.col').length){ html += html }
|
||||
c = (tmp = on.append(html).children('.col')).length;
|
||||
tmp.each(function(){
|
||||
$(this).css('width', (100/c)+'%');
|
||||
})
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Text", combo: ['D','A','T'],
|
||||
on: function(eve){
|
||||
var tag = $('<p>text</p>');
|
||||
meta.tap().append(tag);
|
||||
tag.focus();
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Delete", combo: ['D','A','D'],
|
||||
on: function(eve){
|
||||
meta.tap().remove();
|
||||
}
|
||||
});
|
||||
|
||||
meta.edit({name: "Turn", combo: ['D','T']});
|
||||
meta.edit({name: "Size", combo: ['D','S']});
|
||||
meta.edit({name: "X", combo: ['D','S','X'],
|
||||
on: function(eve){
|
||||
var on = this.a = meta.tap().addClass('meta-on'), was = on.width();
|
||||
$(document).on('mousemove.tmp', function(eve){
|
||||
var be = was + ((eve.pageX||0) - was);
|
||||
on.css({'max-width': be, width: '100%'});
|
||||
});
|
||||
meta.ask('Width in px, %, or other unit?', function(w){
|
||||
if(!w){ return }
|
||||
on.css({'max-width': w, width: '100%'});
|
||||
}, true);
|
||||
}, up: function(){
|
||||
$(document).off('mousemove.tmp');
|
||||
this.a.removeClass('meta-on');
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Y", combo: ['D','S','Y'],
|
||||
//on: function(eve){ console.log('on Y') },
|
||||
on: function(eve){ console.log('use Y')
|
||||
var on = this.a = meta.tap().addClass('meta-on'), was = on.height();
|
||||
$(document).on('mousemove.tmp', function(eve){
|
||||
var be = was + ((eve.pageY||0) - was);
|
||||
on.css({'min-height': be});
|
||||
})
|
||||
}, up: function(){ console.log('up Y')
|
||||
$(document).off('mousemove.tmp');
|
||||
this.a.removeClass('meta-on');
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
var logic = {};
|
||||
|
||||
meta.edit({name: "Logic", combo: ['L']});
|
||||
|
||||
meta.edit({name: "Symbol", combo: ['L','S'],
|
||||
on: function(eve){
|
||||
console.log(1);
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Action", combo: ['L','A'],
|
||||
on: function(eve){
|
||||
console.log(2);
|
||||
}
|
||||
});
|
||||
meta.edit({name: "Data", combo: ['L','D'],
|
||||
on: function(eve){
|
||||
console.log(3);
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
;(function(){
|
||||
var song = {};
|
||||
// TODO:
|
||||
// 1. Manually OR automatically load music.js API, dependencies, and modules. - FINE for now
|
||||
// 2. only export music API, not meta, not dom, not mouselock system, not UI/html, etc. better module isolation and export.
|
||||
// 3. `var wave = Wave('a').play()` // also on `Music.now`
|
||||
// defaults... instrument: pure tones, volume curve: |\_ , speed curve: 0.5
|
||||
// 4. `wave.blur(0.5).itch(0.5);`
|
||||
// 5. wave.long(2); // how long in seconds each note plays, optionally: wave.pace(60) is bpm
|
||||
// 6. wave.loud(0.5); // 0% to 100% volume loudness of device output.
|
||||
// 7. wave.vary(0.5); // slows down or speeds up wiggle per harmonic
|
||||
// 8:
|
||||
// wave structure, does ToneJS allow us to change the sine wave smoothness/type continuously or is it a pre-fixed type?
|
||||
// wave structure: /\/\/, |_|, /|/, \|\| do some research with ToneJS whether these are dynamic or fixed
|
||||
// wave.itch(); // changes the shape of the wiggle from smooth sine to square or triangle
|
||||
// wave.blur(220hz); // blur may not apply/work on pure notes other than filtering them.
|
||||
|
||||
meta.edit({name: "Music", combo: ['M']});
|
||||
|
||||
meta.edit({name: "Play", combo: ['M','P'],
|
||||
on: function(eve){
|
||||
// TODO: We still need to add to meta API ability to change name.
|
||||
if(song.play){
|
||||
music.stop();
|
||||
song.play = false;
|
||||
return;
|
||||
}
|
||||
song.play = true;
|
||||
music.stop();
|
||||
setTimeout(function(){
|
||||
song.now = wave($('#page').text()).play();
|
||||
},250);
|
||||
}
|
||||
});
|
||||
|
||||
meta.edit({name: "Blur", combo: ['M','B'],
|
||||
on: function(eve){
|
||||
$(document).on('mousemove.tmp', function(eve){
|
||||
var x = eve.pageX;
|
||||
song.now.loud(x/$('body').innerWidth());
|
||||
});
|
||||
},
|
||||
up: function(){
|
||||
$(document).off('.tmp');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('keydown', function(eve){
|
||||
if(eve.which === music.which){ return }
|
||||
music.play(String.fromCharCode(music.which = eve.which));
|
||||
});
|
||||
|
||||
}());
|
||||
;(function(){
|
||||
/*
|
||||
Edit
|
||||
Bold
|
||||
Italic
|
||||
Link
|
||||
?
|
||||
Left
|
||||
Middle
|
||||
Right
|
||||
Justify
|
||||
?
|
||||
Small
|
||||
Normal
|
||||
Header
|
||||
Title
|
||||
Design
|
||||
Add
|
||||
Row
|
||||
Column
|
||||
Text
|
||||
Delete
|
||||
Turn
|
||||
Grab
|
||||
Size
|
||||
X
|
||||
Y
|
||||
Fill
|
||||
Logic
|
||||
Symbol
|
||||
Action
|
||||
Data
|
||||
*/
|
||||
/*
|
||||
|
||||
*/
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
439
examples/game/furball.html
Normal file
439
examples/game/furball.html
Normal file
@ -0,0 +1,439 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- always start with these two lines to set a clean baseline for different devices -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
||||
|
||||
<title>Furball</title>
|
||||
</head>
|
||||
<body class="black whitet">
|
||||
<style>
|
||||
/*
|
||||
Choose white text on a black background so you can add color in.
|
||||
Pick your favorite font and choose a font size.
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Mali');
|
||||
html, body {
|
||||
font-family: "Mali", sans-serif;
|
||||
}
|
||||
|
||||
.huef {
|
||||
background: #4D79D8;
|
||||
-webkit-animation: huef 9s infinite;
|
||||
animation: huef 9s infinite;
|
||||
} @keyframes huef {
|
||||
0% {background-color: #4D79D8;}
|
||||
25% {background-color: #33cc33;}
|
||||
50% {background-color: #f2b919;}
|
||||
75% {background-color: #ea3224;}
|
||||
100% {background-color: #4D79D8;}
|
||||
} @-webkit-keyframes huef {
|
||||
0% {background-color: #4D79D8;}
|
||||
25% {background-color: #33cc33;}
|
||||
50% {background-color: #f2b919;}
|
||||
75% {background-color: #ea3224;}
|
||||
100% {background-color: #4D79D8;}
|
||||
}
|
||||
|
||||
button, input {
|
||||
padding: 1em;
|
||||
background: transparent;
|
||||
border: 1px solid white;
|
||||
border-radius: 1.5em;
|
||||
color: white;
|
||||
margin: 0.5em;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover, input:hover {
|
||||
background: white;
|
||||
color: black;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.air { padding-top: 9%; }
|
||||
.yak button { font-size: 80%; }
|
||||
|
||||
.wag {
|
||||
-webkit-animation: wag 3s infinite;
|
||||
animation: wag 3s infinite;
|
||||
} @keyframes wag {
|
||||
0% {transform: rotate(0deg);}
|
||||
50% {transform: rotate(-1deg);}
|
||||
100% {transform: rotate(0deg);}
|
||||
}
|
||||
|
||||
@keyframes print {
|
||||
0% { overflow: hidden; height: 0vh; }
|
||||
99% { overflow: hidden; height: 100vh; }
|
||||
100% { overflow: visible; height: auto; }
|
||||
}
|
||||
input {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
<!-- for educational sites, consider starting with a nice full screen welcome message -->
|
||||
<div class="home hold full huef center air">
|
||||
<div class="focus row">
|
||||
<p><i>Neon ERA presents</i></p>
|
||||
<p class="shout wag">Furball Forces</p>
|
||||
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
|
||||
<div>
|
||||
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
|
||||
<a class="unit yak" href="#choose"><button>PLAY GAME</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="focus center row leak">
|
||||
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
|
||||
<img class="unit blink" src="file:///Users/mark/Pictures/supercatdog.png" style="min-width: 10em; width: 80%;">
|
||||
</div>
|
||||
<script>location.hash = ''</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
|
||||
<script>;(function(){
|
||||
// OPTIONAL MUSIC:
|
||||
$('.home button').on('click', function(){
|
||||
if(window.screen.height > window.screen.width){ return }
|
||||
$('body').append("<div id='audio' onclick='$(this).remove();'><iframe width='0' height='0' src='https://www.youtube-nocookie.com/embed/LLPoZGX0qZk?autoplay=1' frameborder='0'></iframe></div>");
|
||||
})
|
||||
}());
|
||||
</script>
|
||||
<style>#audio { padding: 0.5em; position: fixed; bottom: 0; left: 0; } #audio:before { content: '\25BA'; } #audio:hover:before { content: '\25FC'; }</style>
|
||||
</div>
|
||||
|
||||
<div id="choose" class="hold full hue4 center air">
|
||||
<div class="focus row">
|
||||
<p class="shout wag fur">Choose Team:</p>
|
||||
<div>
|
||||
<a class="unit yak" href="#automecha"><button style="background: white; color: black;">#AutoMecha</button></a>
|
||||
<a class="unit yak" href="#cyberninjas"><button style="background: black; color: white; border-color: black;">#CyberNinjas</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="focus center row leak">
|
||||
<img class="unit blink" src="file:///Users/mark/Pictures/supercatdog.png" style="transform: scaleX(-1); filter: invert(1); min-width: 10em; width: 80%;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas" class="hold full black">
|
||||
<style>
|
||||
#cyberninjas:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Episode 1: Waking</p>
|
||||
<p>"How long until they're online?"</p>
|
||||
<p>"We're copying the soul files, almost done."</p>
|
||||
<p>"Monsters are on the bridge, we do not have time!"</p>
|
||||
<p>"The new body is printing now, it'll be able to outrun them all, just hold on."</p>
|
||||
<p>"It won't know where to run! We're risking ruining the whole resistance, I need to talk to it now."</p>
|
||||
<p>"95% done." The voice behind the glass turns to the soul in the body, "My cub, can you hear me?"</p>
|
||||
<p>...</p>
|
||||
<a class="unit yak" href="#cyberninjas2"><button>Reply "Yes, Mom?"</button></a>
|
||||
</div>
|
||||
<script>
|
||||
;(function(){
|
||||
$('#cyberninjas a').on('click', function(){
|
||||
$('#hud .life').removeClass('down');
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas2" class="hold full red">
|
||||
<style>
|
||||
#cyberninjas2:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
|
||||
#hud {
|
||||
opacity: 0.4;
|
||||
font-family: 'Audiowide', cursive;
|
||||
z-index: 999999999999;
|
||||
transition: all 3s;
|
||||
}
|
||||
#hud .life {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 0px;
|
||||
padding: 0.25em 1em 0.1em;
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
text-shadow: 0em -0.125em 0.75em white;
|
||||
}
|
||||
#hud .score {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 0px;
|
||||
padding: 0.1em 1em 0.25em;
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
text-shadow: 0em 0.1em 0.75em white;
|
||||
}
|
||||
#hud .down {
|
||||
bottom: -2em !important;
|
||||
}
|
||||
#hud .up {
|
||||
top: -2em !important;
|
||||
}
|
||||
</style>
|
||||
<div id="hud">
|
||||
<div class="score shade up">
|
||||
SCORE: <span id="hudscore">0</span>%
|
||||
</div>
|
||||
<div class="life shade down">
|
||||
LIFE: <span id="hudlife">50</span>%
|
||||
</div>
|
||||
</div>
|
||||
<div class="story pad">
|
||||
<p>A fire explodes in the room behind the glass as an AutoMecha blows the door open.</p>
|
||||
<p>The floor shakes and the bed crashes through the wall, flying out of the building.</p>
|
||||
<p class="center">"Mom!!!"</p>
|
||||
<p>There is a total free fall from 10 levels up, water down below.</p>
|
||||
<p>...</p>
|
||||
<a class="unit yak" href="#cyberninjas3"><button>Dive or Die</button></a>
|
||||
</div>
|
||||
<script>
|
||||
;(function(){
|
||||
$('#cyberninjas2 a').on('click', function(){
|
||||
$('#hudlife').text($('#hudlife').data().is = 50);
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas3" class="hold full blue">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=Audiowide');
|
||||
|
||||
#cyberninjas3:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p>The water splashes, swelling and swirling all around.</p>
|
||||
<p>...</p>
|
||||
<p class="center">Rapidly tap to swim up to air:</p>
|
||||
<a class="unit yak"><button>Swim</button></a>
|
||||
</div>
|
||||
<script>
|
||||
;(function(){
|
||||
var go, life = $('#hudlife').data();
|
||||
$('#cyberninjas3 a').on('click', function(){
|
||||
$('#hudlife').text(life.is += 5);
|
||||
if(100 <= life.is){
|
||||
location.hash = 'cyberninjas4';
|
||||
clearInterval(go);
|
||||
go = false;
|
||||
return;
|
||||
}
|
||||
if(go){ return }
|
||||
go = setInterval(function(){
|
||||
if(0 >= life.is){
|
||||
location.hash = 'cyberninjas2';
|
||||
$('#hudlife').text(life.is = 50);
|
||||
clearInterval(go);
|
||||
go = false;
|
||||
return;
|
||||
}
|
||||
$('#hudlife').text(life.is -= 5);
|
||||
}, 1000); // 1 second
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas4" class="hold full black">
|
||||
<style>
|
||||
#cyberninjas4:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Episode 2: Who Am I?</p>
|
||||
<p>"Grab on!" A voice calls out from the darkness.</p>
|
||||
<p>A life vest hits the water and floats within arm's distance.</p>
|
||||
<p>The shivering body is pulled up onto the boat.</p>
|
||||
<p>"Wow, you're heavier than you look. Are you OK? What's your name?"</p>
|
||||
<p>...</p>
|
||||
<p class="center">Write your reply & hit enter:</p>
|
||||
<form class="center">
|
||||
<input class="loud" style="width: 60%;">
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
;(function(){
|
||||
$('form').on('submit', function(eve){ eve.preventDefault() });
|
||||
$('#cyberninjas4').on('submit', function(){
|
||||
var name = $(this).find('input').val();
|
||||
if(!name.length){ return }
|
||||
$('.story-name').text(' '+(window.NAME = name));
|
||||
$('#hud .score').removeClass('up');
|
||||
location.hash = 'cyberninjas5';
|
||||
})
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas5" class="hold full green">
|
||||
<style>
|
||||
#cyberninjas5:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p>"Well<span class="story-name"></span>, it's a miracle you did not die in the building explosion or from that fall."</p>
|
||||
<p>"What is going on? What happened?"</p>
|
||||
<p>"You can't remember? Your brain must be knocked up pretty hard."</p>
|
||||
<p>"No, I was mid copy into this body and now my memories are glitching."</p>
|
||||
<p>"Woah, you're one of those pro elite AREION revolutionaries? All flesh & blood! Dense, too. I would've assumed they were stealing AutoMecha tech for that instead."</p>
|
||||
<p>"I was about to be told vital data for the resistance, but then they blew up the build--"</p>
|
||||
<p>...</p>
|
||||
<p>"Hey, what's the matter?"</p>
|
||||
<p>"My mom. She was in there. I need to go back. Please, help me and tell me everything you know."</p>
|
||||
<p>"I'm so sorry. I can only get so close with the boat, you're gonna have to jump over a lot of broken bits. You ready?"</p>
|
||||
<p>...</p>
|
||||
<a class="unit yak" href="#cyberninjas6"><button>GO!</button></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cyberninjas6" class="hold full green">
|
||||
<style>
|
||||
#cyberninjas6:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="center">... to be continued ...</p>
|
||||
<div id="player" style="position: fixed; width: 1em; height: 1em; background: white; left: 50%; top: 50%; border-radius: 100%;"></div>
|
||||
<!-- jumping game ? like offline dinosaur ? -->
|
||||
</div>
|
||||
<script src="../../../gun/lib/meta.js"></script>
|
||||
<script>
|
||||
;(function(){
|
||||
var p = $('#player');
|
||||
p.x = 50;
|
||||
p.y = 50;
|
||||
meta.edit({
|
||||
name: "Up",
|
||||
combo: ["W"],
|
||||
on: function(){
|
||||
console.log("up");
|
||||
this.to = this.to || setInterval(this.on, 100);
|
||||
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
|
||||
p.css({top: --p.y +'%'});
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ clearTimeout(this.to); this.to = 0 }
|
||||
});
|
||||
meta.edit({
|
||||
name: "Left",
|
||||
combo: ["A"],
|
||||
on: function(){
|
||||
console.log("left");
|
||||
this.to = this.to || setInterval(this.on, 100);
|
||||
p.css({left: --p.x +'%'});
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ clearTimeout(this.to); this.to = 0 }
|
||||
});
|
||||
meta.edit({
|
||||
name: "Down",
|
||||
combo: ["S"],
|
||||
on: function on(){
|
||||
console.log("down");
|
||||
this.to = this.to || setInterval(this.on, 100);
|
||||
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()+100 }, 100);
|
||||
p.css({top: ++p.y +'%'});
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ clearTimeout(this.to); this.to = 0 }
|
||||
});
|
||||
meta.edit({
|
||||
name: "Right",
|
||||
combo: ["D"],
|
||||
on: function(){
|
||||
console.log("right");
|
||||
this.to = this.to || setInterval(this.on, 100);
|
||||
p.css({left: ++p.x +'%'});
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ clearTimeout(this.to); this.to = 0 }
|
||||
});
|
||||
meta.edit({
|
||||
name: "Jump",
|
||||
combo: [32],
|
||||
on: function(){ console.log("jump") },
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Crouch",
|
||||
combo: [16],
|
||||
on: function(){ console.log("crouch") },
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Use",
|
||||
combo: ["E"],
|
||||
on: function(){ console.log("use") },
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Fire",
|
||||
combo: ["F"],
|
||||
on: function(){ console.log("fire") },
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Switch",
|
||||
combo: [9],
|
||||
on: function(){ console.log("Switch") },
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
window.requestAnimationFrame = window.requestAnimationFrame || setTimeout;
|
||||
window.requestAnimationFrame(function frame(){
|
||||
window.requestAnimationFrame(frame, 16);
|
||||
|
||||
}, 16);
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="automecha" class="hold full white blackt">
|
||||
<style>
|
||||
#automecha:target .story {
|
||||
animation: print 3s steps(50, end);
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Episode 1: Training</p>
|
||||
<p>...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hold black center">
|
||||
<div class="pad">
|
||||
<div class="left">
|
||||
<p class="loud">For <i>You</i>,</p>
|
||||
<p>Crafted with love, <span class="redt">♥</span> by ERA.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://era.eco/media/world.png" class="row">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="text-align: center;">
|
||||
<h1 id="when" style="font-size: 7vw; margin-top: 43vh;"></h1>
|
||||
<h1 id="when" style="font-size: 7vw; margin-top: 43vh; font-family: monospace;"></h1>
|
||||
</body>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="/gun/nts.js"></script>
|
||||
@ -16,4 +16,4 @@
|
||||
when.innerHTML = print;
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -10,12 +10,12 @@
|
||||
<p id="debug" style="position: fixed; bottom: 0px; color: white; height: 1em;"></p>
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/nts.js"></script>
|
||||
<!-- script src="../../../gun/nts.js"></script -->
|
||||
<script src="../../../gun/lib/webrtc.js"></script>
|
||||
<script>
|
||||
// Thanks to https://github.com/dmcinnes/HTML5-Asteroids
|
||||
//var gun = Gun();
|
||||
var gun = Gun(location.host? location.origin+'/gun' : 'http://localhost:8765/gun');
|
||||
var gun = GUN(location.origin + '/gun');
|
||||
var game = {gun: gun.get('example/game/space'), area: {}, ships: {}};
|
||||
game.keys = {38: 'up', 37: 'left', 39: 'right', 40: 'down', 32: 'space'};
|
||||
$(document).on('keydown', function(e){
|
||||
@ -50,7 +50,7 @@
|
||||
}
|
||||
game.ship = function(d, el){
|
||||
if(!d){ // spawn our ship
|
||||
var id = Gun.text.random(1, 'abcdefghijklmno');
|
||||
var id = String.random(1, 'abcdefghijklmno');
|
||||
game.me = game.ships[id] = {data: d = {id: id, t: game.now}};
|
||||
game.me.gun = game.gun.get('players').get(d.id).put(d);
|
||||
return;
|
||||
@ -101,7 +101,8 @@
|
||||
s.ay = 0;
|
||||
}
|
||||
|
||||
Gun.obj.map(game.ships, function(ship){
|
||||
Object.keys(game.ships).forEach(function(key, ship){
|
||||
ship = game.ships[key];
|
||||
if(ship.gun){ return }
|
||||
if(!ship.l){ return }
|
||||
if(ship.x-50 <= s.x && s.x <= ship.x+50
|
||||
@ -131,7 +132,7 @@
|
||||
var d = s.data;
|
||||
var dt = (now - d.t) || 0;
|
||||
if(dt > 30 * 1000){
|
||||
Gun.obj.del(game.ships, d.id);
|
||||
delete game.ships[d.id];
|
||||
s.$.remove();
|
||||
return true;
|
||||
}
|
||||
@ -143,7 +144,7 @@
|
||||
}
|
||||
s.x = d.x + d.vx * dt;
|
||||
s.y = d.y + d.vy * dt;
|
||||
|
||||
|
||||
s.x = s.x % area.x;
|
||||
if(s.x < 0){
|
||||
s.x += area.x;
|
||||
@ -159,13 +160,13 @@
|
||||
}
|
||||
return s;
|
||||
}
|
||||
localStorage.clear();
|
||||
game.sync = function(shoot){
|
||||
var me = game.me;
|
||||
if(!me || me.boom){ return }
|
||||
var keys = game.keys;
|
||||
if(shoot || keys.up || keys.right || keys.left || keys.down){
|
||||
var data = Gun.obj.to(me.data);
|
||||
var data = {};
|
||||
Object.keys(me.data).forEach(function(k){ data[k] = me.data[k] }); // 1 layer clone.
|
||||
data.x = data.x / game.area.x;
|
||||
data.y = data.y / game.area.y;
|
||||
me.gun.put(data);
|
||||
@ -182,7 +183,7 @@
|
||||
game.resize();
|
||||
game.ship();
|
||||
game.gun.get('players').map().on(function(data, id){
|
||||
data = Gun.obj.copy(data);
|
||||
data = JSON.parse(JSON.stringify(data)); // clone object, this is bad perf tho.
|
||||
data.x = data.x * game.area.x;
|
||||
data.y = data.y * game.area.y;
|
||||
data.id = data.id || id;
|
||||
@ -196,7 +197,8 @@
|
||||
now = game.now = Gun.state();
|
||||
diff = now - last;
|
||||
last = now;
|
||||
Gun.obj.map(ships, function(ship){
|
||||
Object.keys(ships).forEach(function(key, ship){
|
||||
ship = ships[key];
|
||||
if(!ship.frame){ return }
|
||||
ship.frame(diff, now);
|
||||
});
|
||||
@ -210,7 +212,9 @@
|
||||
gun.on('hi', function(peer){
|
||||
console.log("hi!", peer);
|
||||
if(peer.url){ return }
|
||||
Gun.obj.map(gun.back('opt.peers'), function(peer){
|
||||
var peers = gun.back('opt.peers');
|
||||
Object.keys(peers).forEach(function(id, peer){
|
||||
peer = peers[id];
|
||||
if(!peer.url || !peer.wire){ return }
|
||||
peer.wire._send = peer.wire.send;
|
||||
peer.wire.send = send;
|
||||
@ -221,7 +225,7 @@
|
||||
});
|
||||
function send(raw){
|
||||
if(!raw){ return }
|
||||
if(raw.indexOf('webrtc') >= 0){
|
||||
if(raw.indexOf('rtc') >= 0){
|
||||
if(!this._send){ return }
|
||||
return this._send(raw);
|
||||
}
|
||||
@ -274,4 +278,4 @@
|
||||
left: -50px;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
</html>
|
||||
|
331
examples/game/win.html
Normal file
331
examples/game/win.html
Normal file
@ -0,0 +1,331 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- always start with these two lines to set a clean baseline for different devices -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
<!-- link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/gun/examples/style.css" -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
||||
|
||||
<title>Win</title>
|
||||
</head>
|
||||
<body class="black whitet">
|
||||
<style>
|
||||
/*
|
||||
Choose white text on a black background so you can add color in.
|
||||
Pick your favorite font and choose a font size.
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Montserrat');
|
||||
html, body {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
button, input {
|
||||
padding: 1em;
|
||||
background: transparent;
|
||||
border: 1px solid white;
|
||||
border-radius: 1.5em;
|
||||
color: white;
|
||||
margin: 0.5em;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover, input:hover {
|
||||
background: white;
|
||||
color: black;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.air { padding-top: 9%; }
|
||||
.yak button { font-size: 80%; }
|
||||
.wag {
|
||||
-webkit-animation: wag 3s infinite;
|
||||
animation: wag 3s infinite;
|
||||
} @keyframes wag {
|
||||
0% {transform: rotate(0deg);}
|
||||
50% {transform: rotate(-1deg);}
|
||||
100% {transform: rotate(0deg);}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
<!-- for educational sites, consider starting with a nice full screen welcome message -->
|
||||
<div class="home hold full huef center air">
|
||||
<div class="focus row">
|
||||
<p><i>how to</i></p>
|
||||
<p class="shout wag">Win at Life!</p>
|
||||
<p><i>success, fame, power.</i></p>
|
||||
<p><i>sex, ethics, & integrity.</i></p>
|
||||
<!-- just like in real life, say who you are and give a concise reason why you add value to someone's life and then make a call to action, if they want to learn more they can always scroll to learn more -->
|
||||
<div>
|
||||
<!-- a class="unit hold" href="#fullscreen"><button>WATCH TRAILER</button></a -->
|
||||
<a class="unit yak gap" href="#breathe"><button>PLAY GAME</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="focus center row leak">
|
||||
<!-- just like in real life, looking pretty attracts attention, so show off and look glamorous! -->
|
||||
<img class="unit blink" src="" style="min-width: 10em; width: 80%;">
|
||||
</div>
|
||||
<script>location.hash = ''</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/lib/fun.js"></script>
|
||||
</div>
|
||||
|
||||
<div id="breathe" class="hold full green">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Step 1: Breathe</p>
|
||||
<a class="unit yak" href="#water"><button>Yupe</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="water" class="hold full blue">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Step 2: Drink Water</p>
|
||||
<p></p>
|
||||
<a class="unit yak" href="#eat"><button>Next</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="eat" class="hold full red">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Step 3: Eat Once a Day</p>
|
||||
<p>If you do not want to be eaten, do not eat things that would not want to be eaten.</p>
|
||||
<a class="unit yak" href="#babies"><button>Got It</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="babies" class="hold full red">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Step 4: Babymaking* 😉</p>
|
||||
<p>Find a willing player.</p>
|
||||
<p><small> * This does not always make babies.</small></p>
|
||||
<a class="unit yak" href="#make"><button>How?</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="make" class="hold full hue">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Step 5: Make Dance</p>
|
||||
<p>Moving your body is how you express your thoughts.</p>
|
||||
<p>What you do with your body is what others will come to know you for. So do well.</p>
|
||||
<p>You can make art, songs, or stories; You can make science, tools, or discoveries.</p>
|
||||
<p>Who are you?</p>
|
||||
<a class="unit yak" href="#science"><button>I am a Scientist!</button></a>
|
||||
<a class="unit yak" href="#art"><button>I am an Artist!</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="science" class="hold full hue">
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Science: Knowing Games</p>
|
||||
<p>If you must win one game, it should be the game of making games.</p>
|
||||
<p>If you can make any game, then you will know how to win any game.</p>
|
||||
<a class="unit yak" href="#games"><button>Games?</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="games" class="hold full hue">
|
||||
<div class="story pad">
|
||||
<p>Games have goals and play.</p>
|
||||
<p>Play is a safe space to try new dances.</p>
|
||||
<p>Goals try to get players to do a type of dance.</p>
|
||||
<a class="unit yak" href="#swim"><button>Start</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="swim" class="hold full blue">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=Audiowide');
|
||||
#hud {
|
||||
opacity: 0.4;
|
||||
font-family: 'Audiowide', cursive;
|
||||
z-index: 999999999999;
|
||||
transition: all 3s;
|
||||
}
|
||||
#hud .life {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 0px;
|
||||
padding: 0.25em 1em 0.1em;
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
text-shadow: 0em -0.125em 0.75em white;
|
||||
}
|
||||
#hud .score {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 0px;
|
||||
padding: 0.1em 1em 0.25em;
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
text-shadow: 0em 0.1em 0.75em white;
|
||||
}
|
||||
#hud .down {
|
||||
bottom: -2em !important;
|
||||
}
|
||||
#hud .up {
|
||||
top: -2em !important;
|
||||
}
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p>The simplest goal is to not "die" in the game.</p>
|
||||
<p>Oh look, you've fallen into water and cannot breathe.</p>
|
||||
<p>If you do not push the button to swim to the top, you'll lose the game.</p>
|
||||
<a class="unit yak"><button>Swim</button></a>
|
||||
</div>
|
||||
<div id="hud">
|
||||
<div class="score shade up">
|
||||
SCORE: <span id="hudscore">0</span>%
|
||||
</div>
|
||||
<div class="life shade down">
|
||||
LIFE: <span id="hudlife">100</span>%
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
;(function(){
|
||||
var go, life = $('#hudlife').data();
|
||||
$(window).on('hashchange', function(){
|
||||
if(location.hash != '#swim'){ return }
|
||||
$('#hudlife').text($('#hudlife').data().is = 100);
|
||||
$('#hud .life').removeClass('down');
|
||||
go = setInterval(function(){
|
||||
if(0 >= life.is){
|
||||
location.hash = 'die';
|
||||
clearInterval(go);
|
||||
go = false;
|
||||
return;
|
||||
}
|
||||
$('#hudlife').text(life.is -= 1);
|
||||
}, 100);
|
||||
})
|
||||
$('#swim a').on('click', function(){
|
||||
$('#hudlife').text((life.is += 5) < 100? life.is : 100);
|
||||
if(100 <= life.is){
|
||||
location.hash = 'won';
|
||||
clearInterval(go);
|
||||
go = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="won" class="hold full hue">
|
||||
<div class="story pad">
|
||||
<p>You won your first game! 🎉</p>
|
||||
<p>See? I told you breathing is important.</p>
|
||||
<p>You also learned the most basic dance: rapid poking.</p>
|
||||
<p>The goal of the next game is to make the game you just won.</p>
|
||||
<a class="unit yak" href="#won" style="z-index: 999999;"><button>Make</button></a>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/gh/amark/gun/lib/meta.js"></script>
|
||||
<script>
|
||||
meta.edit({name: "Add", combo: ['A']});
|
||||
meta.edit({
|
||||
name: "Timer",
|
||||
combo: ['A','T'],
|
||||
on: function(){
|
||||
console.log("up");
|
||||
this.to = this.to || setInterval(this.on, 100);
|
||||
$("html, body").stop().animate({ scrollTop: $(window).scrollTop()-100 }, 100);
|
||||
p.css({top: --p.y +'%'});
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ clearTimeout(this.to); this.to = 0 }
|
||||
});
|
||||
meta.edit({
|
||||
name: "Delete",
|
||||
combo: ['A','D'],
|
||||
on: function(){
|
||||
$(meta.tap.on).remove();
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Button",
|
||||
combo: ['A','B'],
|
||||
on: function(){
|
||||
$(meta.tap.on).append("<a class='unit yak'><button>Button</button></a>")
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){}
|
||||
});
|
||||
meta.edit({
|
||||
name: "Edit",
|
||||
combo: ['E'],
|
||||
on: function(){
|
||||
$('body').attr('contenteditable', 'true' == $('body').attr('contenteditable')? false : true);
|
||||
},
|
||||
use: function(){},
|
||||
up: function(){ }
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
window.requestAnimationFrame(function frame(){
|
||||
return;
|
||||
window.requestAnimationFrame(frame);
|
||||
if(location.hash != '#won'){ return }
|
||||
var p = $('#won a').offset();
|
||||
var bx = p.left, by = p.top, mx = meta.tap.x, my = meta.tap.y;
|
||||
bx = mx - bx; by = my - by;
|
||||
var d = Math.sqrt(bx*bx + by*by);
|
||||
console.log(bx, by, mx, my, d);
|
||||
if(d > 250){ return }
|
||||
$('#won a').css({position: 'fixed', left: bx + (Math.random()*100), top: by + (Math.random()*100)});
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="rules" class="hold full white blackt">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">Bend these Rules if it is more Moral to do so</p>
|
||||
<p></p>
|
||||
<a class="unit yak" href="#water"><button>Next</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="die" class="hold full red">
|
||||
<style>
|
||||
</style>
|
||||
<div class="story pad">
|
||||
<p class="loud crack">GAME OVER</p>
|
||||
<p></p>
|
||||
<a class="unit yak" href="#make"><button>Start Over</button></a>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,66 +0,0 @@
|
||||
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
|
||||
|
||||
var Gun = require('../');
|
||||
|
||||
// have to do this before instancing gun(?)
|
||||
Gun.on('out', function(msg){
|
||||
this.to.next( msg );
|
||||
msg = JSON.stringify(msg);
|
||||
gunPeers.forEach( function(peer){ peer.send( msg ) })
|
||||
})
|
||||
|
||||
var gun = Gun({
|
||||
file: 'data.json'
|
||||
});
|
||||
|
||||
var server = require('http').createServer(function(req, res){
|
||||
var insert = "";
|
||||
if( req.url.endsWith( "gun.js" ) )
|
||||
insert = "../";
|
||||
|
||||
require('fs').createReadStream(require('path').join(__dirname, insert, req.url)).on('error',function(){ // static files!
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end(require('fs').readFileSync(require('path').join(__dirname, 'index.html'))); // or default to index
|
||||
}).pipe(res); // stream
|
||||
});
|
||||
|
||||
// do not do this to attach server... instead pull websocket provider and use that.
|
||||
// gun.wsp(server);
|
||||
|
||||
var ws = require( 'ws' ); // default websocket provider gun used...
|
||||
var WebSocketServer = ws.Server;
|
||||
|
||||
var wss = new WebSocketServer( {
|
||||
server: server, // 'ws' npm
|
||||
autoAcceptConnections : false // want to handle the request (websocket npm?)
|
||||
});
|
||||
|
||||
wss.on('connection',acceptConnection )
|
||||
|
||||
var gunPeers = []; // used as a list of connected clients.
|
||||
|
||||
function acceptConnection( connection ) {
|
||||
// connection.upgradeReq.headers['sec-websocket-protocol'] === (if present) protocol requested by client
|
||||
// connection.upgradeReq.url === url request
|
||||
console.log( "connect?", connection.upgradeReq.headers, connection.upgradeReq.url )
|
||||
gunPeers.push( connection );
|
||||
connection.on( 'error',function(error){console.log( "WebSocket Error:", error) } );
|
||||
|
||||
connection.on('message', function (msg) {
|
||||
msg = JSON.parse(msg)
|
||||
if ("forEach" in msg) msg.forEach(m => gun.on('in', JSON.parse(m)));
|
||||
else gun.on('in', msg)
|
||||
})
|
||||
|
||||
connection.on( 'close', function(reason,desc){
|
||||
// gunpeers gone.
|
||||
var i = gunPeers.findIndex( function(p){return p===connection} );
|
||||
if( i >= 0 )
|
||||
gunPeers.splice( i, 1 );
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
server.listen(port);
|
||||
|
||||
console.log('Server started on port ' + port + ' with ');
|
@ -1,21 +1,34 @@
|
||||
;(function(){
|
||||
var cluster = require('cluster');
|
||||
if(cluster.isMaster){
|
||||
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
|
||||
}
|
||||
|
||||
var fs = require('fs');
|
||||
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
|
||||
var Gun = require('../'); // require('gun')
|
||||
|
||||
if(process.env.HTTPS_KEY){
|
||||
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
||||
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
||||
} else {
|
||||
config.server = require('http').createServer(Gun.serve(__dirname));
|
||||
}
|
||||
|
||||
var gun = Gun({web: config.server.listen(config.port) });
|
||||
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
||||
;(function(){
|
||||
var cluster = require('cluster');
|
||||
if(cluster.isMaster){
|
||||
return cluster.fork() && cluster.on('exit',function(){ cluster.fork(); require('../lib/crashed') });
|
||||
}
|
||||
|
||||
var fs = require('fs'), env = process.env;
|
||||
var GUN = require('../'); // require('gun');
|
||||
var opt = {
|
||||
port: env.PORT || process.argv[2] || 8765,
|
||||
peers: env.PEERS && env.PEERS.split(',') || []
|
||||
};
|
||||
|
||||
if(fs.existsSync((opt.home = require('os').homedir())+'/cert.pem')){
|
||||
env.HTTPS_KEY = env.HTTPS_KEY || opt.home+'/key.pem';
|
||||
env.HTTPS_CERT = env.HTTPS_CERT || opt.home+'/cert.pem';
|
||||
}
|
||||
if(env.HTTPS_KEY){
|
||||
opt.port = 443;
|
||||
opt.key = fs.readFileSync(env.HTTPS_KEY);
|
||||
opt.cert = fs.readFileSync(env.HTTPS_CERT);
|
||||
opt.server = require('https').createServer(opt, GUN.serve(__dirname));
|
||||
require('http').createServer(function(req, res){
|
||||
res.writeHead(301, {"Location": "https://"+req.headers['host']+req.url });
|
||||
res.end();
|
||||
}).listen(80);
|
||||
} else {
|
||||
opt.server = require('http').createServer(GUN.serve(__dirname));
|
||||
}
|
||||
|
||||
var gun = GUN({web: opt.server.listen(opt.port), peers: opt.peers});
|
||||
console.log('Relay peer started on port ' + opt.port + ' with /gun');
|
||||
module.exports = gun;
|
||||
}());
|
8
examples/https.sh
Normal file
8
examples/https.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
cd ~
|
||||
git clone https://github.com/acmesh-official/acme.sh.git
|
||||
cd ~/acme.sh
|
||||
./acme.sh --install -m $EMAIL
|
||||
|
||||
bash ~/acme.sh/acme.sh --issue -d $DOMAIN -w $WEB
|
||||
bash ~/acme.sh/acme.sh --install-cert -d $DOMAIN --key-file ~/key.pem --fullchain-file ~/cert.pem --reloadcmd "service relay force-reload"
|
@ -1,28 +1,4 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>gun examples</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- h1>Examples Directory <button style="float: right;" onclick="localStorage.clear()">Clear Local Storage</button></h1 -->
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
border: none;
|
||||
b-order-top: ridge 2em skyblue;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<a href="/todo/index.html"><iframe src="/todo/index.html"></iframe></a>
|
||||
<!-- a href="/json/index.html"><iframe src="/json/index.html"></iframe></a -->
|
||||
<a href="/chat/index.html"><iframe src="/chat/index.html"></iframe></a>
|
||||
<!-- script src="../gun.js"></script -->
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<p>This is the examples folder.
|
||||
<p>The most basic example is <a href="./basic/note.html">./basic/note.html</a>!</p>
|
||||
<p>Home page temporarily disabled.</p>
|
116
examples/infinite-scroll/ScrollWindow.js
Normal file
116
examples/infinite-scroll/ScrollWindow.js
Normal file
@ -0,0 +1,116 @@
|
||||
const DEFAULT_OPTIONS = {
|
||||
size: 20,
|
||||
stickTo: 'top',
|
||||
};
|
||||
|
||||
class ScrollWindow {
|
||||
constructor(gunNode, opts = {}) {
|
||||
this.opts = Object.assign(DEFAULT_OPTIONS, opts);
|
||||
this.elements = new Map();
|
||||
this.node = gunNode;
|
||||
this.center = this.opts.startAt;
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
|
||||
updateSubscriptions() {
|
||||
this.upSubscription && this.upSubscription.off();
|
||||
this.downSubscription && this.downSubscription.off();
|
||||
|
||||
const subscribe = params => {
|
||||
this.node.get({ '.': params}).map().on((val, key, a, eve) => {
|
||||
if (params['-']) {
|
||||
this.downSubscription = eve;
|
||||
} else {
|
||||
this.upSubscription = eve;
|
||||
}
|
||||
this._addElement(key, val);
|
||||
});
|
||||
};
|
||||
|
||||
if (this.center) {
|
||||
subscribe({ '>': this.center, '<': '\uffff' });
|
||||
subscribe({'<': this.center, '>' : '', '-': true});
|
||||
} else {
|
||||
subscribe({ '<': '\uffff', '>': '', '-': this.opts.stickTo === 'top' });
|
||||
}
|
||||
}
|
||||
|
||||
_getSortedKeys() {
|
||||
this.sortedKeys = this.sortedKeys || [...this.elements.keys()].sort();
|
||||
return this.sortedKeys;
|
||||
}
|
||||
|
||||
_upOrDown(n, up) {
|
||||
this.opts.stickTo = null;
|
||||
const keys = this._getSortedKeys();
|
||||
n = n || (keys.length / 2);
|
||||
n = up ? n : -n;
|
||||
const half = Math.floor(keys.length / 2);
|
||||
const newMiddleIndex = Math.max(Math.min(half + n, keys.length - 1), 0);
|
||||
if (this.center !== keys[newMiddleIndex]) {
|
||||
this.center = keys[newMiddleIndex];
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
return this.center;
|
||||
}
|
||||
|
||||
up(n) {
|
||||
return this._upOrDown(n, true);
|
||||
}
|
||||
|
||||
down(n) {
|
||||
return this._upOrDown(n, false);
|
||||
}
|
||||
|
||||
_topOrBottom(top) {
|
||||
this.opts.stickTo = top ? 'top' : 'bottom';
|
||||
this.center = null;
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
|
||||
top() {
|
||||
this._topOrBottom(true);
|
||||
}
|
||||
|
||||
bottom() {
|
||||
this._topOrBottom(false);
|
||||
}
|
||||
|
||||
_addElement(key, val) {
|
||||
if (!val || this.elements.has(key)) return;
|
||||
const add = () => {
|
||||
this.elements.set(key, val);
|
||||
this.sortedKeys = [...this.elements.keys()].sort();
|
||||
const sortedElements = this.sortedKeys.map(k => this.elements.get(k));
|
||||
this.opts.onChange && this.opts.onChange(sortedElements);
|
||||
};
|
||||
const keys = this._getSortedKeys();
|
||||
if (keys.length < this.opts.size) {
|
||||
add();
|
||||
} else {
|
||||
if (this.opts.stickTo === 'top' && key > keys[0]) {
|
||||
this.elements.delete(keys[0]);
|
||||
add();
|
||||
} else if (this.opts.stickTo === 'bottom' && key < keys[keys.length - 1]) {
|
||||
this.elements.delete(keys[keys.length - 1]);
|
||||
add();
|
||||
} else if (this.center) {
|
||||
if (keys.indexOf(this.center) < (keys.length / 2)) {
|
||||
if (key < keys[keys.length - 1]) {
|
||||
this.elements.delete(keys[keys.length - 1]);
|
||||
add();
|
||||
}
|
||||
} else {
|
||||
if (key > keys[0]) {
|
||||
delete this.elements.delete(keys[0]);
|
||||
add();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getElements() {
|
||||
return this.elements;
|
||||
}
|
||||
}
|
30
examples/infinite-scroll/index.html
Normal file
30
examples/infinite-scroll/index.html
Normal file
@ -0,0 +1,30 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Infinite scroll example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css">
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="./ScrollWindow.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<form id="generate">
|
||||
<input type="text" id="number" placeholder="Number of posts"/>
|
||||
<button>Generate</button>
|
||||
</form>
|
||||
<div id="top-buttons">
|
||||
<button id="top">Top</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="top-sentinel" style="padding-top: 0px;"></div>
|
||||
<div id="container"></div>
|
||||
<div id="bottom-sentinel" style="padding-top: 0px;"></div>
|
||||
|
||||
<button id="bottom">Bottom</button>
|
||||
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
167
examples/infinite-scroll/index.js
Normal file
167
examples/infinite-scroll/index.js
Normal file
@ -0,0 +1,167 @@
|
||||
const gun = new Gun();
|
||||
|
||||
const size = 20;
|
||||
const gunNode = gun.get('posts');
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
};
|
||||
|
||||
let topSentinelPreviousY = 0;
|
||||
let topSentinelPreviousRatio = 0;
|
||||
let bottomSentinelPreviousY = 0;
|
||||
let bottomSentinelPreviousRatio = 0;
|
||||
let previousUpIndex = previousDownIndex = -1;
|
||||
|
||||
const render = elements => {
|
||||
const t = new Date();
|
||||
elements.reverse().forEach((data, j) => {
|
||||
var date = new Date(data.date);
|
||||
$('#date' + j).text(date.toLocaleDateString() + ' ' + date.toLocaleTimeString());
|
||||
$('#text' + j).text(data.text);
|
||||
$('#img' + j).attr('src', '');
|
||||
$('#img' + j).attr('src', _getCatImg(date.getTime()));
|
||||
$('#post' + j).css({visibility: 'visible'});
|
||||
});
|
||||
console.log('rendering took', new Date().getTime() - t.getTime(), 'ms');
|
||||
window.onRender && window.onRender(elements);
|
||||
};
|
||||
|
||||
const onChange = debounce(render, 20);
|
||||
|
||||
const scroller = new ScrollWindow(gunNode, {size, stickTo: 'top', onChange});
|
||||
|
||||
const initList = () => {
|
||||
for (var n = 0; n < size; n++) {
|
||||
var el = $("<div>").addClass('post').attr('id', 'post' + n).css({visibility: 'hidden'});
|
||||
el.append($('<b>').attr('id', 'date' + n));
|
||||
el.append($('<span>').attr('id', 'text' + n));
|
||||
el.append($('<img>').attr('id', 'img' + n).attr('height', 100).attr('width', 100));
|
||||
$('#container').append(el);
|
||||
}
|
||||
}
|
||||
|
||||
const _getCatImg = (n) => {
|
||||
const url = "https://source.unsplash.com/collection/139386/100x100/?sig=";
|
||||
return url + n % 999999;
|
||||
};
|
||||
|
||||
const getNumFromStyle = numStr => Number(numStr.substring(0, numStr.length - 2));
|
||||
|
||||
const adjustPaddings = isScrollDown => {
|
||||
const container = document.getElementById("container");
|
||||
const currentPaddingTop = getNumFromStyle(container.style.paddingTop);
|
||||
const currentPaddingBottom = getNumFromStyle(container.style.paddingBottom);
|
||||
const remPaddingsVal = 198 * (size / 2); // TODO: calculate actual element heights
|
||||
if (isScrollDown) {
|
||||
container.style.paddingTop = currentPaddingTop + remPaddingsVal + "px";
|
||||
container.style.paddingBottom = currentPaddingBottom === 0 ? "0px" : currentPaddingBottom - remPaddingsVal + "px";
|
||||
} else {
|
||||
container.style.paddingBottom = currentPaddingBottom + remPaddingsVal + "px";
|
||||
if (currentPaddingTop === 0) {
|
||||
$(window).scrollTop($('#post0').offset().top + remPaddingsVal);
|
||||
} else {
|
||||
container.style.paddingTop = currentPaddingTop - remPaddingsVal + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const topSentCallback = entry => {
|
||||
const container = document.getElementById("container");
|
||||
|
||||
const currentY = entry.boundingClientRect.top;
|
||||
const currentRatio = entry.intersectionRatio;
|
||||
const isIntersecting = entry.isIntersecting;
|
||||
|
||||
// conditional check for Scrolling up
|
||||
if (
|
||||
currentY > topSentinelPreviousY &&
|
||||
isIntersecting &&
|
||||
currentRatio >= topSentinelPreviousRatio &&
|
||||
scroller.center !== previousUpIndex && // stop if no new results were received
|
||||
scroller.opts.stickTo !== 'top'
|
||||
) {
|
||||
previousUpIndex = scroller.center;
|
||||
adjustPaddings(false);
|
||||
scroller.up(size / 2);
|
||||
}
|
||||
topSentinelPreviousY = currentY;
|
||||
topSentinelPreviousRatio = currentRatio;
|
||||
}
|
||||
|
||||
const botSentCallback = entry => {
|
||||
const currentY = entry.boundingClientRect.top;
|
||||
const currentRatio = entry.intersectionRatio;
|
||||
const isIntersecting = entry.isIntersecting;
|
||||
|
||||
// conditional check for Scrolling down
|
||||
if (
|
||||
currentY < bottomSentinelPreviousY &&
|
||||
currentRatio > bottomSentinelPreviousRatio &&
|
||||
isIntersecting &&
|
||||
scroller.center !== previousDownIndex && // stop if no new results were received
|
||||
scroller.opts.stickTo !== 'bottom'
|
||||
) {
|
||||
previousDownIndex = scroller.center;
|
||||
adjustPaddings(true);
|
||||
scroller.down(size / 2);
|
||||
}
|
||||
bottomSentinelPreviousY = currentY;
|
||||
bottomSentinelPreviousRatio = currentRatio;
|
||||
}
|
||||
|
||||
const initIntersectionObserver = () => {
|
||||
const options = {
|
||||
//rootMargin: '190px',
|
||||
}
|
||||
|
||||
const callback = entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.target.id === 'post0') {
|
||||
topSentCallback(entry);
|
||||
} else if (entry.target.id === `post${size - 1}`) {
|
||||
botSentCallback(entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var observer = new IntersectionObserver(callback, options); // TODO: It's possible to quickly scroll past the sentinels without them firing. Top and bottom sentinels should extend to page top & bottom?
|
||||
observer.observe(document.querySelector("#post0"));
|
||||
observer.observe(document.querySelector(`#post${size - 1}`));
|
||||
}
|
||||
|
||||
initList(size);
|
||||
initIntersectionObserver();
|
||||
|
||||
$('#top').click(() => {
|
||||
scroller.top();
|
||||
$('#container').css({'padding-top': 0, 'padding-bottom': 0});
|
||||
$(document.body).animate({ scrollTop: 0 }, 500);
|
||||
});
|
||||
$('#bottom').click(() => {
|
||||
scroller.bottom();
|
||||
$('#container').css({'padding-top': 0, 'padding-bottom': 0});
|
||||
$(document.body).animate({ scrollTop: $("#container").height() }, 500);
|
||||
});
|
||||
|
||||
$('#generate').submit(e => {
|
||||
e.preventDefault();
|
||||
const day = 24 * 60 * 60 * 1000;
|
||||
const year = 365 * day;
|
||||
const n = Number($('#number').val());
|
||||
for (let i = 0; i < n; i++) {
|
||||
const d = new Date(40 * year + i * day).toISOString();
|
||||
gunNode.get(d).put({text: 'Hello world!', date: d});
|
||||
}
|
||||
});
|
77
examples/infinite-scroll/style.css
Normal file
77
examples/infinite-scroll/style.css
Normal file
@ -0,0 +1,77 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 65px;
|
||||
}
|
||||
|
||||
header {
|
||||
background: rgba(255,255,255,0.75);
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0;
|
||||
}
|
||||
|
||||
#bottom {
|
||||
position: fixed;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
border: 0;
|
||||
background-color: #efefef;
|
||||
padding: 15px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4a4f9d;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: white;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
input, button {
|
||||
outline: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input:focus, button:focus, button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#top-buttons, form {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#top-buttons {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.post {
|
||||
margin: 15px;
|
||||
padding: 15px;
|
||||
background-color: #9de1fe;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.post b {
|
||||
margin-right: 5;
|
||||
}
|
||||
|
||||
.post img {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
}
|
@ -3,25 +3,51 @@
|
||||
# README
|
||||
# This will install nodejs and npm on your system,
|
||||
# should work on most places other than Windows.
|
||||
# For it to run on boot as a server, a recent OS is needed.
|
||||
# Set any environment variables before you run this,
|
||||
# like `export RAD=false` to disable storage, or
|
||||
# pass file paths of `HTTPS_CERT` & `HTTPS_KEY`, etc.
|
||||
# Copy paste and run each line into your terminal.
|
||||
# If you are on Windows, http://nodejs.org/download/ has
|
||||
# an installer that will automatically do it for you.
|
||||
# curl -o- https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
|
||||
# wget -O - https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
|
||||
|
||||
#debian/ubuntu
|
||||
cd ~
|
||||
apt-get install sudo -y
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install curl git git-core -y
|
||||
sudo apt-get install curl git git-core systemd -y
|
||||
sudo apt-get install systemctl -y
|
||||
#fedora/openSUSE
|
||||
sudo yum check-update -y
|
||||
sudo yum install curl git git-core -y
|
||||
sudo yum install curl git git-core systemd -y
|
||||
sudo yum install systemctl -y
|
||||
|
||||
#screen -S install # You can safely CTRL+A+D to escape without stopping the process. `screen -R install` to resume. Stop all with `killall screen`. Note: May need to `sudo apt-get install screen`
|
||||
|
||||
# install nodejs
|
||||
git clone http://github.com/isaacs/nave.git
|
||||
sudo ./nave/nave.sh usemain stable
|
||||
git clone https://github.com/isaacs/nave.git
|
||||
./nave/nave.sh usemain stable
|
||||
|
||||
# If you just want nodejs and npm but not gun, stop here.
|
||||
|
||||
npm install gun
|
||||
|
||||
# to run the gun examples:
|
||||
cd ./node_modules/gun
|
||||
#npm install gun@latest
|
||||
#cd ./node_modules/gun
|
||||
mkdir node_modules
|
||||
git clone https://github.com/amark/gun.git
|
||||
cd gun
|
||||
git checkout .
|
||||
git pull
|
||||
git checkout master
|
||||
git checkout $VERSION
|
||||
git pull
|
||||
npm install .
|
||||
sudo /usr/local/bin/node ./examples/http.js 80 # change `80` to `8765` for development purposes.
|
||||
|
||||
cp ./examples/relay.service /lib/systemd/system/relay.service
|
||||
echo $PWD >> /lib/systemd/system/relay.service
|
||||
echo "fs.file-max = 999999" >> /etc/sysctl.conf
|
||||
ulimit -u unlimited
|
||||
sysctl -p /etc/sysctl.conf
|
||||
systemctl daemon-reload
|
||||
systemctl enable relay
|
||||
systemctl restart relay
|
1
examples/iris
Symbolic link
1
examples/iris
Symbolic link
@ -0,0 +1 @@
|
||||
../node_modules/iris-messenger/src
|
@ -58,7 +58,7 @@
|
||||
color: skyblue;
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
cursor: poiner;
|
||||
cursor: pointer;
|
||||
}
|
||||
ul, li {
|
||||
list-style-type: none;
|
||||
|
@ -178,21 +178,21 @@
|
||||
});
|
||||
$('#share').addClass("hide");
|
||||
} else {
|
||||
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || Gun.text.random(5)); // trick with cookies!
|
||||
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || String.random(5)); // trick with cookies!
|
||||
gps.ref = gun.get('gps/' + gps.track);
|
||||
gps.opt.track = function(pos){
|
||||
pos = pos.latlng;
|
||||
if(gps.follow
|
||||
|| Gun.time.is() - gps.when < 1000
|
||||
|| Gun.state() - gps.when < 1000
|
||||
|| gps.last && gps.last.lat == pos.lat && gps.last.lng == pos.lng){
|
||||
return; // throttle!
|
||||
}
|
||||
gps.when = Gun.time.is();
|
||||
gps.when = Gun.state();
|
||||
gps.ref.put(gps.last = pos);
|
||||
//$('#debug').value = JSON.stringify(gps.last);
|
||||
}
|
||||
gps.where = gps.where || Where(gps.opt);
|
||||
$('#follow').text(("where.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track);
|
||||
$('#follow').text((location.origin + location.pathname) + '#' + gps.track);
|
||||
$('#share').removeClass("hide");
|
||||
$('#share').on('click', function(){
|
||||
$('#link').toggleClass("hide");
|
||||
|
@ -4,7 +4,7 @@
|
||||
<title>Party by Neon ERA</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Raleway:100" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Caveat" rel="stylesheet">
|
||||
<style>
|
||||
.write {
|
||||
@ -38,10 +38,12 @@
|
||||
;(() => {
|
||||
function S(){};
|
||||
window.S = S;
|
||||
try{localStorage.clear();//sessionStorage.clear();
|
||||
}catch(e){}
|
||||
S.gun = Gun(location.host? location.origin+'/gun' : 'http://localhost:8765/gun');
|
||||
//S.gun = Gun('http://localhost:8765/gun');
|
||||
//S.gun = Gun();
|
||||
S.app = S.gun.get('examples/social/1');
|
||||
S.app = S.gun.get('examples/social/2');
|
||||
S.user = S.gun.user();
|
||||
S.tell = (what, n) => {
|
||||
var e = $('#tell').find('p');
|
||||
@ -67,13 +69,30 @@
|
||||
#hi .faces img {
|
||||
w-idth: 5%;
|
||||
width: 3em;
|
||||
width: 7vh;
|
||||
}
|
||||
#hi .ton {
|
||||
border-radius: 1em;
|
||||
font-size: 150%;
|
||||
font-size: 2.25vmax;
|
||||
margin: 0 0.5em 0 0.5em;
|
||||
background: transparent;
|
||||
border: 1px solid white;
|
||||
color: white;
|
||||
font-family: 'Raleway', sans-serif;
|
||||
}
|
||||
#hi .ton:hover {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
<div class="loud write shout rim">Party by NEON ERA.</div>
|
||||
<div class="loud write shout rim">Join the Private Party!</div>
|
||||
<div id="faces" class="flush faces">
|
||||
<div class="right" style="max-width: 20em;">
|
||||
<input id="halias" class="write jot sap" placeholder="username">
|
||||
<input id="hpass" type="password" class="write jot sap" placeholder="passphrase">
|
||||
<div class="right" style="max-width: 30em;">
|
||||
<a href="chrome://extensions" target="_blank"><button class="ton">Install Now</button></a>
|
||||
<a href="#"><button class="ton">How It Works</button></a>
|
||||
<!-- input id="halias" class="write jot sap" placeholder="username">
|
||||
<input id="hpass" type="password" class="write jot sap" placeholder="passphrase" -->
|
||||
<script>
|
||||
$.as.route.page('hi', () => {
|
||||
$('#hpass').on('focus', () => {
|
||||
@ -134,6 +153,14 @@
|
||||
return faces;
|
||||
});
|
||||
</script>
|
||||
<div class="pad ditch">
|
||||
<p class="loud"><i>Your friend has invited you to add a privacy extension to your browser:</i></p>
|
||||
<p> - Decrypts your friends' messages across any site!</p>
|
||||
<p> - Stops tech monopolies from selling your private data to advertisers.</p>
|
||||
<p> - Gives you ownership and control over all your data online.</p>
|
||||
<p> - Creates a searchable history of your posts, friends, and more!</p>
|
||||
</div>
|
||||
<div id="faces2" class="flush faces"></div>
|
||||
<div class="pad ditch">
|
||||
<p class="loud"><i>Express your thoughts & connect with the world around you!</i></p>
|
||||
<p> - Discover new relationships.</p>
|
||||
@ -141,12 +168,10 @@
|
||||
<p> - Watch fun videos and photos from people who share.</p>
|
||||
<p> - But this time, you own it: fully decentralized.</p>
|
||||
</div>
|
||||
<div id="faces2" class="flush faces">
|
||||
</div>
|
||||
<div class="pad ditch" style="margin-top: 1em;">
|
||||
<p><span class="loud write shout">Welcome</span><span class="write loud">, you are currently connected to <b id="peers" class="huet4">2</b> peers. <b>Why not try to sign up or log in?</b></span></p>
|
||||
<p><span class="loud write shout">Welcome,</span><!-- span class="write loud">, you are currently connected to <b id="peers" class="huet4">2</b> peers. <b>Why not try to sign up or log in?</b></span --></p>
|
||||
<p> - Your identity is created here, by you. Not on a server.</p>
|
||||
<p> - It uses secure <a href="https://gun.eco/explainers/data/security.html">cryptographic</a> methods to protect you.</p>
|
||||
<p> - It uses secure <a href="https://gun.eco/docs/Cartoon-Cryptography">cryptographic</a> methods to protect you.</p>
|
||||
<p> - Only you have access to it, meaning even we cannot reset your password!</p>
|
||||
<p> - For added security, you can freely <a href="https://github.com/amark/gun">download</a> and run it on your own computer.</p>
|
||||
</div>
|
||||
@ -530,7 +555,7 @@
|
||||
</ul>
|
||||
<div class="model">
|
||||
<li class="spoke tint sap gully">
|
||||
<div class="gap"><span class="sort none"></span><img class="face act none"><b class="name"></b><span class="what"></span></div>
|
||||
<div class="gap"><span class="sort none"></span><img class="face act none"><b class="name"></b><div class="what"></div></div>
|
||||
</li>
|
||||
</div>
|
||||
<div style="height: 10%;"></div>
|
||||
@ -547,23 +572,33 @@
|
||||
});
|
||||
window.user = S.user;
|
||||
$('#speak').on('submit', (e) => {
|
||||
/*var say = normalize($('#speak .draft'));
|
||||
console.log(1, say.html());
|
||||
return;*/
|
||||
var say = $('#speak .draft').text();
|
||||
var say = $('#speak .draft').text(); //.text(); // NO NO NO NO NO
|
||||
if(!say){ return }
|
||||
var ref = S.user.get('who').get('all').set({what: say});
|
||||
ref.get('by').put(S.user.get('who'));
|
||||
S.user.get('who').get('said').time(ref);
|
||||
S.gun.get('@').time(ref);
|
||||
console.log('save!', say);
|
||||
var ref = S.user.get('who').get('all').set({what: say, when: Gun.state()});
|
||||
//ref.get('by').put(S.user.get('who'));
|
||||
//S.user.get('who').get('said').time(ref);
|
||||
S.user.get('who').get('said').set(ref);
|
||||
//S.gun.get('@').time(ref);
|
||||
$('#speak .draft').text('');
|
||||
});
|
||||
S.gun.get('@').time(async (data, key, time) => {
|
||||
var ref = S.gun.get(data), tmp;
|
||||
var said = await ref.then();
|
||||
//S.gun.get('@').time(async (data, key, time) => {
|
||||
S.user.get('who').get('said').map().once(async (data, key, time) => {
|
||||
//var ref = S.gun.get(data), tmp;
|
||||
//var said = await ref.then();
|
||||
key = key.replace(/[^A-Za-z]/ig,'');
|
||||
var tmp, said = data, time = said.when;
|
||||
var $li = $($('#'+key)[0] || $('#draft .model .spoke').clone(true,true).attr('id', key)[(tmp = $.as.sort(time, $('#draft ul').children('li').first()))[0]?'insertBefore':'appendTo'](tmp[0] || '#draft ul'));
|
||||
$li.find('.what').text(said.what);
|
||||
var by = ref.get('by');
|
||||
tmp = said.what;
|
||||
if(tmp && tmp.ct){
|
||||
tmp = JSON.stringify(tmp);
|
||||
setTimeout(async function(){
|
||||
tmp = await SEA.decrypt(said.what, S.user._.sea);
|
||||
$li.find('.what').text(tmp);
|
||||
}, 750);
|
||||
}
|
||||
$li.find('.what').text(tmp); // NORMALIAZE!!!
|
||||
var by = S.user.get('who');// ref.get('by');
|
||||
by.get('face').get('small').on(data => {
|
||||
$li.find('.face').attr('src', data).removeClass('none');
|
||||
});
|
||||
@ -579,7 +614,7 @@
|
||||
if(face){
|
||||
$li.find('.face').attr('src', face).removeClass('none');
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
$(document).on('click', '#speak .act.face', (eve) => {
|
||||
|
||||
});
|
||||
@ -587,61 +622,80 @@
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="create" class="white center act">
|
||||
<div id="contacts" class="hue2 page">
|
||||
<style>
|
||||
#create {
|
||||
position: fixed;
|
||||
bottom: 2em;
|
||||
right: 2em;
|
||||
font-size: 1em;
|
||||
font-family: Tahoma, arial;
|
||||
border-radius: 1em;
|
||||
z-index: 99999;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
opacity: 0.8;
|
||||
#contacts ul .face {
|
||||
float: left;
|
||||
border-radius: 100%;
|
||||
vertical-align: middle;
|
||||
height: 2.5em;
|
||||
margin-right: 5%;
|
||||
}
|
||||
#contacts ul, #contacts li {
|
||||
overflow: visible;
|
||||
transition: all 0.2s ease-in;
|
||||
}
|
||||
#create:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#create span {
|
||||
line-height: 2em;
|
||||
}
|
||||
#create .menu {
|
||||
display: none;
|
||||
height: 10em;
|
||||
width: 10em;
|
||||
bottom: 0em;
|
||||
right: 0em;
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
border-bottom-right-radius: 1em;
|
||||
#contacts .who {
|
||||
display: inline-block;
|
||||
margin-right: 2%;
|
||||
min-width: 250px;
|
||||
width: 15em;
|
||||
text-align: left;
|
||||
padding: 2%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#create:hover .menu {
|
||||
display: block;
|
||||
#contacts .what {
|
||||
}
|
||||
#create li {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
#create a {
|
||||
color: black;
|
||||
}
|
||||
#create a:hover {
|
||||
color: #4D79D8;
|
||||
#contacts .spoke {
|
||||
}
|
||||
</style>
|
||||
<span class="huet act">+</span>
|
||||
<div class="white sap menu" style="width: 7em;">
|
||||
<ul class="left blackt gap">
|
||||
<a href="#out" class="act"><li>Sign Out</li></a>
|
||||
<a href="#settings" class="act"><li>Settings</li></a>
|
||||
<li>Profile</li>
|
||||
<li>Help</li>
|
||||
</ul>
|
||||
<p class="pad">Contacts</p>
|
||||
<ul class="mid row col center">
|
||||
</ul>
|
||||
<div class="model">
|
||||
<li class="who tint sap gully">
|
||||
<div class="gap">
|
||||
<span class="sort none"></span>
|
||||
<img class="face act none" crossOrigin="Anonymous">
|
||||
<big class="name"></big><br/>
|
||||
<i class="alias"></i><br/>
|
||||
<small>FB ID:</small> <span class="fbid"></span>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
<div style="height: 10%;"></div>
|
||||
<script>
|
||||
$.as.route.page('contacts', () => {
|
||||
if(!S.user.is){ return $.as.route('sign') }
|
||||
//S.gun.get('@').time(async (data, key, time) => {
|
||||
|
||||
// TODO: BUG!! switch from `on` to `once` to get Martti's {_} empty object bug.
|
||||
var $ul = $('#contacts ul');
|
||||
S.user.get('old').get('fb').map().on(async function(data){
|
||||
console.log('contact:', data);
|
||||
var key = data.fbid, tmp;
|
||||
if(!key){ return }
|
||||
var $li = $($('#fbid'+key)[0] || $('#contacts .model .who').clone(true,true).attr('id', 'fbid'+key)[(tmp = $.as.sort(data.name||1, $ul.children('li').first()))[0]?'insertBefore':'appendTo'](tmp[0] || $ul));
|
||||
|
||||
$li.find('.name').text(data.name);
|
||||
$li.find('.alias').text(data.alias);
|
||||
$li.find('.fbid').text(data.fbid);
|
||||
tmp = $li.find('img').attr('src', data.face || data.tmp);
|
||||
if(!data.face){
|
||||
var ref = this;
|
||||
$.fn.upload.shrink(data.tmp, function(b64){
|
||||
ref.get('face').put(b64);
|
||||
}, 100);
|
||||
}
|
||||
$('html, body').stop(true, true).animate({scrollTop: $ul.height()});
|
||||
});
|
||||
function img2b64(img, cb){
|
||||
var c = document.createElement('canvas');
|
||||
var ctx = c.getContext("2d");
|
||||
ctx.drawImage(img, 10, 10);
|
||||
cb(c.toDataURL());
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="tell" class="center">
|
||||
@ -673,6 +727,7 @@
|
||||
</style>
|
||||
<p class="mid black">Hello world!</p>
|
||||
</div>
|
||||
<!-- textarea id='debug' class="no-ne" style="position: fixed; bottom: 0; left: 0; width: 25%; height: 30%"></textarea -->
|
||||
|
||||
<script>
|
||||
$.as.route.page('person', () => {
|
||||
@ -686,7 +741,7 @@
|
||||
if(e.err){ return }
|
||||
var m = $($("#d"+e.id)[0] || $('#d0').clone(true,true).attr('id', 'd'+e.id).css('backgroundImage', '').appendTo('#draft')).addClass('pulse');
|
||||
if(up){ return up.shrink(e, resize, 1000) }
|
||||
console.log(e.id, e.base64);
|
||||
//console.log(e.id, e.base64);
|
||||
m.removeClass('pulse').css({
|
||||
backgroundImage: 'url(' + e.base64 + ')',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
@ -697,5 +752,7 @@
|
||||
</script>
|
||||
<script async src="../../gun/lib/fun.js"></script>
|
||||
<script async src="../../gun/lib/normalize.js"></script>
|
||||
<script async src="../../gun/lib/monotype.js"></script>
|
||||
<script async src="../../gun/lib/meta.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -10,7 +10,7 @@ import { fromObjects, toObjects } from './asyncSerialize';
|
||||
import { subtle } from './compat';
|
||||
export function parse(text) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// need decodeURIComponent so binary strings are transfered properly
|
||||
// need decodeURIComponent so binary strings are transferred properly
|
||||
const deocodedText = unescape(text);
|
||||
const objects = JSON.parse(deocodedText);
|
||||
return fromObjects(serializers(true), objects);
|
||||
@ -19,7 +19,7 @@ export function parse(text) {
|
||||
export function stringify(value, waitForArrayBufferView = true) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const serialized = yield toObjects(serializers(waitForArrayBufferView), value);
|
||||
// need encodeURIComponent so binary strings are transfered properly
|
||||
// need encodeURIComponent so binary strings are transferred properly
|
||||
const message = JSON.stringify(serialized);
|
||||
return escape(message);
|
||||
});
|
||||
@ -147,7 +147,7 @@ const CryptoKeySerializer = {
|
||||
};
|
||||
}),
|
||||
fromObject: (cks) => __awaiter(this, void 0, void 0, function* () {
|
||||
// if we don't have access to to a real crypto implementation, just return
|
||||
// if we don't have access to a real crypto implementation, just return
|
||||
// the serialized crypto key
|
||||
if (crypto.fake) {
|
||||
const newCks = Object.assign({}, cks);
|
||||
|
18
examples/react/.gitignore
vendored
18
examples/react/.gitignore
vendored
@ -1,18 +0,0 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "gun-react-examples",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"gun": "file:../..",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"express": "^4.15.2",
|
||||
"express-http-proxy": "^0.11.0",
|
||||
"react-scripts": "0.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject",
|
||||
"server": "PORT=8081 node ./server.js"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
console.log("If modules not found, run `npm install` in /example folder!");
|
||||
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
|
||||
var host = process.env.OPENSHIFT_NODEJS_HOST || process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';
|
||||
|
||||
var express = require('express');
|
||||
var proxy = require('express-http-proxy');
|
||||
var http = require('http');
|
||||
var app = express();
|
||||
var server = http.createServer(app);
|
||||
|
||||
var Gun = require('gun');
|
||||
var gun = Gun({
|
||||
file: 'data.json',
|
||||
web: server
|
||||
});
|
||||
|
||||
app.use(Gun.serve);
|
||||
app.use(proxy(host + ':8765'));
|
||||
server.listen(port);
|
||||
|
||||
console.log('Server started on port ' + port + ' with /gun');
|
39
examples/react/todo.html
Normal file
39
examples/react/todo.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<script type="text/babel">
|
||||
const gun = Gun();
|
||||
const App = () => {
|
||||
const newTodo = React.useRef()
|
||||
const [todos, setTodos] = React.useState({})
|
||||
|
||||
React.useEffect(() => {
|
||||
return gun
|
||||
.get("todos")
|
||||
.map()
|
||||
.on((todo, id) => setTodos(todos => ({...todos, [id]: todo }))).off;
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<title>TODOs</title>
|
||||
<ul>{Object.values(todos).map(({title}, i) => <li key={i}>{title}</li>)}</ul>
|
||||
<form onSubmit={e => {
|
||||
e.preventDefault();
|
||||
gun.get("todos").set({ title: newTodo.current.value });
|
||||
newTodo.current.value = ''
|
||||
}}>
|
||||
<input ref={newTodo} placeholder="new todo"/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
ReactDOM.render(<App />, document.getElementById("app"));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
19
examples/relay.service
Normal file
19
examples/relay.service
Normal file
@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=GUN relay
|
||||
Documentation=https://gun.eco
|
||||
After=network.target
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
[Service]
|
||||
Environment=PATH=/usr/bin:/usr/local/bin
|
||||
LimitNOFILE=infinity
|
||||
LimitNPROC=infinity
|
||||
LimitCORE=infinity
|
||||
StartLimitBurst=999999
|
||||
StartLimitIntervalSec=999999
|
||||
Restart=always
|
||||
ExecStart=node examples/http.js 80
|
||||
# Environment=NODE_ENV=production
|
||||
WorkingDirectory=
|
1104
examples/smoothie.js
Normal file
1104
examples/smoothie.js
Normal file
File diff suppressed because it is too large
Load Diff
22
examples/start.js.html
Normal file
22
examples/start.js.html
Normal file
@ -0,0 +1,22 @@
|
||||
/*<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body></body>
|
||||
<script>// */
|
||||
;(function(){try{
|
||||
if(typeof window == "undefined"){ return }
|
||||
var url = location.hash.slice(1);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(e){
|
||||
if(4 != xhr.readyState){ return }
|
||||
var d = document, doc = d.createElement("html");
|
||||
doc.innerHTML = xhr.responseText;
|
||||
var head = doc.getElementsByTagName('head')[0] || doc.appendChild(d.createElement('head'));
|
||||
var base = doc.getElementsByTagName('base')[0] || head.appendChild(d.createElement('base'));
|
||||
base.href = url;
|
||||
document.write(doc.outerHTML);
|
||||
};
|
||||
xhr.open('GET', url, true);
|
||||
xhr.send(null);
|
||||
}catch(e){document.write(''+e)}}());
|
||||
//</script></html>
|
153
examples/stats.html
Normal file
153
examples/stats.html
Normal file
@ -0,0 +1,153 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
font-family: helvetica;
|
||||
background-color: rgb(25,25,25);
|
||||
color: rgb(80,135,25) !important;
|
||||
text-shadow: 1px 1px 20px rgb(80,150,25);
|
||||
}
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
left: 0.5em;
|
||||
top: 1.75em;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 30px;
|
||||
padding:10px;
|
||||
background-color: rgb(50,50,50);
|
||||
color: rgb(250,50,50);
|
||||
}
|
||||
|
||||
.tall { height: 5em; }
|
||||
</style>
|
||||
|
||||
<div class="center"><span class="shout" id="peers">0</span> peers <span class="shout" id="time">0</span> min <span class="shout" id="nodes">0</span> nodes <span class="shout" id="hours">0</span> hours <span class="shout" id="block">0</span> block <span class="shout" id="stack">0</span> stack</div>
|
||||
|
||||
<input id="url" class="center input crack" placeholder="enter peer stats source url">
|
||||
|
||||
<div class="center row charts">
|
||||
</div>
|
||||
|
||||
<div class="model none">
|
||||
<div class="chart"><span class="label"></span><canvas class="tall row"></canvas></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./jquery.js"></script>
|
||||
<script src="./smoothie.js" charset="utf-8"></script>
|
||||
<script>
|
||||
if(window.location.search){url.value = window.location.search.split("?")[1]}
|
||||
var up, br = 0, bt = 0, tmp;
|
||||
var fetchData = async function(){
|
||||
// fetch the data from server
|
||||
var S = +new Date;
|
||||
var data = await (await fetch(url.value||(location.origin+'/gun/stats.radata'), {method: 'GET',mode: 'cors'})).json();
|
||||
$('#block').text(((br += (+new Date - S)/1000) / ++bt).toFixed(1));
|
||||
data.over = (data.over/1000) || 15;
|
||||
$('#stack').text((data.cpu||'').stack);
|
||||
$('#peers').text(data.peers.count);
|
||||
$('#time').text((data.peers.time / 1000 / 60).toFixed(0));
|
||||
$('#nodes').text(data.node.count);
|
||||
$('#hours').text((data.up.time / 60 / 60).toFixed(1));
|
||||
if(data.up.time === up){ console.log("up same as before") } up = data.up.time;
|
||||
|
||||
;(async function(){ try{
|
||||
Stats('peers#').line.append(+new Date, data.peers.count);
|
||||
return;
|
||||
Stats('cpu%');
|
||||
tmp = await (await fetch(new Request(location.origin+'/gun/stats.top.radata'), {method: 'GET',mode: 'cors'})).text();
|
||||
tmp = parseFloat((tmp.split('\n').filter(l => l.indexOf('node')+1)[0]||'').split(/\s+/).slice(-4)[0]||'0');
|
||||
Stats('cpu%').line.append(+new Date, tmp);
|
||||
}catch(e){console.log(e)}}());
|
||||
|
||||
Stats('memory').line.append(+new Date, data.memory.heapTotal / 1024 / 1024);
|
||||
try{ Stats('dam # in/s').line.append(+new Date, Math.round(data.dam.in.count / data.over)); }catch(e){}
|
||||
try{ Stats('dam in MB/s').line.append(+new Date, data.dam.in.done / 1024 / 1024 / data.over); }catch(e){}
|
||||
try{ Stats('dam # out/s').line.append(+new Date, Math.round(data.dam.out.count / data.over)); }catch(e){}
|
||||
try{ Stats('dam out MB/s').line.append(+new Date, data.dam.out.done / 1024 / 1024 / data.over); }catch(e){}
|
||||
|
||||
console.log('data',data);
|
||||
|
||||
//fetch keys in all, these may be dynamically changing
|
||||
//for each key, check if we already have created a time series, if not, create it and add it
|
||||
// to the chart corredsponding to the unit of measure
|
||||
$.each(data.all, function(key, arr){
|
||||
var chart = Stats(key);
|
||||
// get data and append to line
|
||||
// get the arrays inside the key
|
||||
//for each array append the data to the line
|
||||
for(var i in arr) {
|
||||
// append data [timestamp], [data]
|
||||
chart.line.append(arr[i][0], arr[i][1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
//setInterval(fetchData, 15 * 1000);
|
||||
setInterval(fetchData, 5000);
|
||||
fetchData();
|
||||
|
||||
function Stats(key, chart){
|
||||
// if we have already created, get data to append to it.
|
||||
if(chart = Stats[key]){
|
||||
return chart;
|
||||
}
|
||||
// create a new Series for this key
|
||||
// add it into the map
|
||||
chart = Stats[key] = new SmoothieChart({millisPerPixel:500, limitFPS: 16, responsive: true, minValue: 0, grid:{strokeStyle:'rgba(100%,100%,100%,0.2)'},labels:{fontSize:20}, grid: {verticalSections: 0, millisPerLine: 15000 * 4 /*, strokeStyle:'rgb(125, 0, 0)'*/}});
|
||||
chart.line = new TimeSeries();
|
||||
chart.addTimeSeries(chart.line,{ strokeStyle:'rgb('+Math.random()*255+', '+Math.random()*255+','+Math.random()*255+')', lineWidth:5 });
|
||||
chart.canvas = $('.model').find('.chart').clone(true).appendTo('.charts');
|
||||
chart.canvas.find('span').text(key);
|
||||
chart.streamTo(chart.canvas.find('canvas').get(0), 15 * 1000);
|
||||
chart.line.append(0, 0);
|
||||
// check first two characters of key to determine other charts to add this in
|
||||
// tbd later
|
||||
return chart;
|
||||
}
|
||||
;(function(){
|
||||
if('https' != (''+location).slice(0,5) && "localhost" != location.hostname){
|
||||
$('body').append("<button id='https'>click here to try generating https certs</button>");
|
||||
if(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/.test(location.hostname)){
|
||||
$('#https').text("Link this IP address to a Domain by adding an `A Record` to your DNS settings that point to `"+ location.hostname +"` (we recommend the `name/host` be any subdomain you want, like `relay`, but if you want the root domain itself to directly point here use `*`). Then come back here on the domain & click this button to generate HTTPS certificates.");
|
||||
return;
|
||||
}
|
||||
$('body').append("<input id='email' placeholder='email'/>");
|
||||
$('#https').on('click', function(){
|
||||
$(this).text("look at console.log for errors, if none, try https");
|
||||
var gun = GUN(location.origin + '/gun');
|
||||
if(!$('#email').val()){
|
||||
$(this).text("email necessary for certs! Type it in & click here again.");
|
||||
return;
|
||||
}
|
||||
setTimeout(function(){
|
||||
gun._.opt.mesh.say({dam: 'service', try: 'https', email: $('#email').val(), domain: location.hostname});
|
||||
setTimeout(function(){
|
||||
if(gun._.opt.mesh.near){ return }
|
||||
$('#https').text("It might have worked! try HTTPS!");
|
||||
}, 9000);
|
||||
}, 999);
|
||||
});
|
||||
}
|
||||
}());
|
||||
/*
|
||||
Notes to Self about Debugging:
|
||||
1. Read Disks can spike up to 1min, I suspect other operations are blocking it from resolving as fast as it otherwise would.
|
||||
2. JSON parsing/stringifying sometimes way slower than other times, why?
|
||||
3. Looks like RAD lex read is not optimized.
|
||||
4. got prep + got emit = non-RAD problems, compare against read disk & got differentials (should be same).
|
||||
5. Radix map/place ops could be slow?
|
||||
6. SINGLE MESSAGE PROCESS TIME occasionally is huge, should get emailed.
|
||||
7. Watch out for get/put loops times, maybe indicating (5) issues?
|
||||
*/
|
||||
</script>
|
||||
<script src="../gun.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,48 +1,57 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Oxygen);
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Oxygen', 'Trebuchet MS', arial;
|
||||
position: relative;
|
||||
background: black;
|
||||
color: white;
|
||||
line-height: 1.5;
|
||||
font-size: 18pt;
|
||||
f-ont-size: max(18pt, 2?vw);
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
div, ul, ol, li, p, span, form, button, input, textarea {
|
||||
div, ul, ol, li, p, span, form, button, input, textarea, img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
vertical-align: inherit;
|
||||
-webkit-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button, input, textarea {
|
||||
background: white;
|
||||
border: none;
|
||||
color: black;
|
||||
a, button, input, textarea {
|
||||
background: inherit;
|
||||
border: inherit;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
input:not([type=button]):not([type=submit]), textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a:focus, button:focus, input[type=button]:focus, input[type=submit]:focus {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
ul, li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
}
|
||||
p + p {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
[contenteditable=true]:empty:before {
|
||||
content: attr(placeholder);
|
||||
}
|
||||
::placeholder, .hint {
|
||||
color: inherit;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.model, .none { display: none }
|
||||
.hide {
|
||||
opacity: 0;
|
||||
@ -50,152 +59,153 @@ ul, li {
|
||||
transition: all 2s;
|
||||
}
|
||||
|
||||
.page {
|
||||
.full {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.max {
|
||||
max-width: 48em;
|
||||
}
|
||||
.min {
|
||||
min-width: 12em;
|
||||
}
|
||||
.pad {
|
||||
margin: 5% auto;
|
||||
min-width: 250px;
|
||||
width: 95%;
|
||||
max-width: 50em;
|
||||
margin: 5% auto;
|
||||
max-width: 48em;
|
||||
min-width: 12em;
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
}
|
||||
.row::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
.col {
|
||||
max-width: 24em;
|
||||
min-width: 12em;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
}
|
||||
.mid {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.flush {
|
||||
line-height: 0em;
|
||||
.top {
|
||||
vertical-align: top;
|
||||
}
|
||||
.low {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.rim {
|
||||
margin: 2%;
|
||||
}
|
||||
.rim { margin: 1%; }
|
||||
.gap {
|
||||
padding: 3%;
|
||||
padding: clamp(0.5em, 3%, 1.5em);
|
||||
}
|
||||
.gully {
|
||||
margin-bottom: 1%;
|
||||
.stack { line-height: 0; }
|
||||
.crack { margin-bottom: 1%; }
|
||||
.sit { margin-bottom: 0; }
|
||||
|
||||
.focus {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
float: none;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.sit { margin-bottom: 0; }
|
||||
.row { width: 100%; }
|
||||
.col { max-width: 33em; }
|
||||
.leak { overflow: visible; }
|
||||
.hold { overflow: hidden; }
|
||||
|
||||
.act {
|
||||
display: block;
|
||||
/*display: block;*/
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
-webkit-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.symbol {
|
||||
|
||||
.unit, .symbol {
|
||||
display: inline-block;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
.sap { border-radius: 0.1em; }
|
||||
.jot { border-bottom: 1px dashed #95B2CA; }
|
||||
|
||||
.loud {
|
||||
font-size: 150%;
|
||||
}
|
||||
.jot {
|
||||
border-bottom: 1px dashed #95B2CA;
|
||||
}
|
||||
.sap {
|
||||
border-radius: 0.1em;
|
||||
.shout {
|
||||
font-size: 36pt;
|
||||
font-size: 6.5vmax;
|
||||
}
|
||||
|
||||
.red { background: #ea3224; }
|
||||
.green { background: #33cc33; }
|
||||
.blue { background: #4D79D8; }
|
||||
.yellow { background: #d3a438; }
|
||||
.black { background: black; }
|
||||
.white { background: white; }
|
||||
|
||||
.red {
|
||||
background: #ea3224;
|
||||
}
|
||||
.green {
|
||||
background: #33cc33;
|
||||
}
|
||||
.blue {
|
||||
background: #4D79D8;
|
||||
}
|
||||
.yellow {
|
||||
background: #f2b919;
|
||||
}
|
||||
.black {
|
||||
background: black;
|
||||
}
|
||||
.white {
|
||||
background: white;
|
||||
}
|
||||
.shade { background: rgba(0%, 0%, 0%, 0.1); }
|
||||
.tint { background: rgba(100%, 100%, 100%, 0.1); }
|
||||
|
||||
.shade {
|
||||
background: rgba(0%, 0%, 0%, 0.1);
|
||||
}
|
||||
.tint {
|
||||
background: rgba(100%, 100%, 100%, 0.1);
|
||||
}
|
||||
|
||||
.redt {
|
||||
color: #ea3224;
|
||||
}
|
||||
.greent {
|
||||
color: #33cc33;
|
||||
}
|
||||
.bluet {
|
||||
color: #4D79D8;
|
||||
}
|
||||
.yellowt {
|
||||
color: #f2b919;
|
||||
}
|
||||
.blackt {
|
||||
color: black;
|
||||
}
|
||||
.whitet {
|
||||
color: white;
|
||||
}
|
||||
.redt { color: #ea3224; }
|
||||
.greent { color: #33cc33; }
|
||||
.bluet { color: #4D79D8; }
|
||||
.yellowt { color: #d3a438; }
|
||||
.blackt { color: black; }
|
||||
.whitet { color: white; }
|
||||
|
||||
.hue {
|
||||
background: #4D79D8;
|
||||
-webkit-animation: hue 900s infinite;
|
||||
animation: hue 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes hue {
|
||||
} @keyframes hue {
|
||||
0% {background-color: #4D79D8;}
|
||||
25% {background-color: #33cc33;}
|
||||
50% {background-color: #f2b919;}
|
||||
50% {background-color: #d3a438;}
|
||||
75% {background-color: #ea3224;}
|
||||
100% {background-color: #4D79D8;}
|
||||
} @-webkit-keyframes hue {
|
||||
0% {background-color: #4D79D8;}
|
||||
25% {background-color: #33cc33;}
|
||||
50% {background-color: #f2b919;}
|
||||
50% {background-color: #d3a438;}
|
||||
75% {background-color: #ea3224;}
|
||||
100% {background-color: #4D79D8;}
|
||||
}
|
||||
|
||||
.huet {
|
||||
color: #4D79D8;
|
||||
color: #4D79D8;
|
||||
-webkit-animation: huet 900s infinite;
|
||||
animation: huet 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes huet {
|
||||
} @keyframes huet {
|
||||
0% {color: #4D79D8;}
|
||||
25% {color: #33cc33;}
|
||||
50% {color: #f2b919;}
|
||||
50% {color: #d3a438;}
|
||||
75% {color: #ea3224;}
|
||||
100% {color: #4D79D8;}
|
||||
} @-webkit-keyframes huet {
|
||||
0% {color: #4D79D8;}
|
||||
25% {color: #33cc33;}
|
||||
50% {color: #f2b919;}
|
||||
50% {color: #d3a438;}
|
||||
75% {color: #ea3224;}
|
||||
100% {color: #4D79D8;}
|
||||
}
|
||||
@ -204,19 +214,17 @@ ul, li {
|
||||
background: #ea3224;
|
||||
-webkit-animation: hue2 900s infinite;
|
||||
animation: hue2 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes hue2 {
|
||||
} @keyframes hue2 {
|
||||
0% {background-color: #ea3224;}
|
||||
25% {background-color: #4D79D8;}
|
||||
50% {background-color: #33cc33;}
|
||||
75% {background-color: #f2b919;}
|
||||
75% {background-color: #d3a438;}
|
||||
100% {background-color: #ea3224;}
|
||||
} @-webkit-keyframes hue2 {
|
||||
0% {background-color: #ea3224;}
|
||||
25% {background-color: #4D79D8;}
|
||||
50% {background-color: #33cc33;}
|
||||
75% {background-color: #f2b919;}
|
||||
75% {background-color: #d3a438;}
|
||||
100% {background-color: #ea3224;}
|
||||
}
|
||||
|
||||
@ -224,19 +232,17 @@ ul, li {
|
||||
color: #ea3224;
|
||||
-webkit-animation: huet2 900s infinite;
|
||||
animation: huet2 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes huet2 {
|
||||
} @keyframes huet2 {
|
||||
0% {color: #ea3224;}
|
||||
25% {color: #4D79D8;}
|
||||
50% {color: #33cc33;}
|
||||
75% {color: #f2b919;}
|
||||
75% {color: #d3a438;}
|
||||
100% {color: #ea3224;}
|
||||
} @-webkit-keyframes huet2 {
|
||||
0% {color: #ea3224;}
|
||||
25% {color: #4D79D8;}
|
||||
50% {color: #33cc33;}
|
||||
75% {color: #f2b919;}
|
||||
75% {color: #d3a438;}
|
||||
100% {color: #ea3224;}
|
||||
}
|
||||
|
||||
@ -244,17 +250,15 @@ ul, li {
|
||||
background: #33cc33;
|
||||
-webkit-animation: hue3 900s infinite;
|
||||
animation: hue3 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes hue3 {
|
||||
} @keyframes hue3 {
|
||||
0% {background-color: #33cc33;}
|
||||
25% {background-color: #f2b919;}
|
||||
25% {background-color: #d3a438;}
|
||||
50% {background-color: #ea3224;}
|
||||
75% {background-color: #4D79D8;}
|
||||
100% {background-color: #33cc33;}
|
||||
} @-webkit-keyframes hue3 {
|
||||
0% {background-color: #33cc33;}
|
||||
25% {background-color: #f2b919;}
|
||||
25% {background-color: #d3a438;}
|
||||
50% {background-color: #ea3224;}
|
||||
75% {background-color: #4D79D8;}
|
||||
100% {background-color: #33cc33;}
|
||||
@ -264,74 +268,64 @@ ul, li {
|
||||
color: #33cc33;
|
||||
-webkit-animation: huet3 900s infinite;
|
||||
animation: huet3 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes huet3 {
|
||||
} @keyframes huet3 {
|
||||
0% {color: #33cc33;}
|
||||
25% {color: #f2b919;}
|
||||
25% {color: #d3a438;}
|
||||
50% {color: #ea3224;}
|
||||
75% {color: #4D79D8;}
|
||||
100% {color: #33cc33;}
|
||||
} @-webkit-keyframes huet3 {
|
||||
0% {color: #33cc33;}
|
||||
25% {color: #f2b919;}
|
||||
25% {color: #d3a438;}
|
||||
50% {color: #ea3224;}
|
||||
75% {color: #4D79D8;}
|
||||
100% {color: #33cc33;}
|
||||
}
|
||||
|
||||
.hue4 {
|
||||
background: #f2b919;
|
||||
background: #d3a438;
|
||||
-webkit-animation: hue4 900s infinite;
|
||||
animation: hue4 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes hue4 {
|
||||
0% {background-color: #f2b919;}
|
||||
} @keyframes hue4 {
|
||||
0% {background-color: #d3a438;}
|
||||
25% {background-color: #ea3224;}
|
||||
50% {background-color: #4D79D8;}
|
||||
75% {background-color: #33cc33;}
|
||||
100% {background-color: #f2b919;}
|
||||
100% {background-color: #d3a438;}
|
||||
} @-webkit-keyframes hue4 {
|
||||
0% {background-color: #f2b919;}
|
||||
0% {background-color: #d3a438;}
|
||||
25% {background-color: #ea3224;}
|
||||
50% {background-color: #4D79D8;}
|
||||
75% {background-color: #33cc33;}
|
||||
100% {background-color: #f2b919;}
|
||||
100% {background-color: #d3a438;}
|
||||
}
|
||||
|
||||
.huet4 {
|
||||
color: #f2b919;
|
||||
color: #d3a438;
|
||||
-webkit-animation: huet4 900s infinite;
|
||||
animation: huet4 900s infinite;
|
||||
}
|
||||
|
||||
@keyframes huet4 {
|
||||
0% {color: #f2b919;}
|
||||
} @keyframes huet4 {
|
||||
0% {color: #d3a438;}
|
||||
25% {color: #ea3224;}
|
||||
50% {color: #4D79D8;}
|
||||
75% {color: #33cc33;}
|
||||
100% {color: #f2b919;}
|
||||
100% {color: #d3a438;}
|
||||
} @-webkit-keyframes huet4 {
|
||||
0% {color: #f2b919;}
|
||||
0% {color: #d3a438;}
|
||||
25% {color: #ea3224;}
|
||||
50% {color: #4D79D8;}
|
||||
75% {color: #33cc33;}
|
||||
100% {color: #f2b919;}
|
||||
100% {color: #d3a438;}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse
|
||||
{
|
||||
} @keyframes pulse {
|
||||
0% {opacity: 1;}
|
||||
50% {opacity: 0.5;}
|
||||
100% {opacity: 1;}
|
||||
}
|
||||
|
||||
|
||||
.joy {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
@ -339,13 +333,20 @@ ul, li {
|
||||
background: url(https://cdn.jsdelivr.net/npm/gun/examples/pop.png) no-repeat;
|
||||
background-position: -2800px 0;
|
||||
pointer-events: none;
|
||||
z-index: 999999999;
|
||||
animation: joy 1s steps(28);
|
||||
} @keyframes joy {
|
||||
0% {background-position: 0 0;}
|
||||
100% {background-position: -2800px 0;}
|
||||
}
|
||||
@keyframes joy {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -2800px 0;
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
border: 0;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
@ -1,83 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Think</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="think" class="hue page">
|
||||
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
#think {
|
||||
font-size: 24pt;
|
||||
font-size: 6vmin;
|
||||
font-family: 'Alegreya Sans', sans-serif;
|
||||
color: white;
|
||||
}
|
||||
#think li {
|
||||
width: 90%;
|
||||
margin-top: 0.3em;
|
||||
border-bottom: 1px dashed white;
|
||||
}
|
||||
#think .add {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1em;
|
||||
padding: 0.5em;
|
||||
font-family: Tahoma, arial;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
#think ul, #think li {
|
||||
list-style-type: circle;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
||||
<div class="pad whitet" style="width: 75%;">
|
||||
<div style="margin-top: 2%;">
|
||||
<div class="rubric left center">Add a Thought...</div>
|
||||
<a href="#" class="right huet white act add">+</a>
|
||||
</div>
|
||||
<ul>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script src="/gun/nts.js"></script>
|
||||
<script>
|
||||
// Check out the interactive tutorial
|
||||
// for how to build a simplified version
|
||||
// of this example: https://scrimba.com/c/cW2Vsa
|
||||
var gun = Gun(location.origin+'/gun');
|
||||
var think = gun.get('think/' + location.hash.slice(1));
|
||||
var typing, throttle;
|
||||
$('.add').on('click', function(){
|
||||
$('<li>').attr('contenteditable', true).prependTo('ul');
|
||||
});
|
||||
$(document).on('keyup', "[contenteditable=true]", function(){
|
||||
var li = $(this), id = li.attr('id');
|
||||
if(!id){
|
||||
li.attr('id', id = Gun.text.random());
|
||||
}
|
||||
typing = id;
|
||||
clearTimeout(throttle);
|
||||
throttle = setTimeout(function(){
|
||||
think.get(id).put(li.text());
|
||||
typing = false;
|
||||
},10);
|
||||
});
|
||||
think.map().on(function(thought, id){
|
||||
var li = $('#'+id)[0] || $('<li>').attr('id', id).attr('contenteditable', true).prependTo('ul');
|
||||
if(thought){
|
||||
if(id === typing){ return }
|
||||
$(li).text(thought);
|
||||
} else {
|
||||
$(li).hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<title>Think</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Alegreya+Sans:300italic' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
.thought {
|
||||
font-family: 'Alegreya Sans', sans-serif;
|
||||
}
|
||||
|
||||
.thought__heading {
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.thought__form-container {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.thought__item {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.thought__input {
|
||||
flex: 1;
|
||||
font-family: 'Alegreya Sans', sans-serif;
|
||||
font-size: 25px;
|
||||
font-weight: 500;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.thought__add {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-family: Tahoma, arial;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
font-size: 25px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.thought__add:hover::after {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.thought__add:focus::after {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.thought__add::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.3s;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.thought__list {
|
||||
list-style-type: circle;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 90px 20px;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@media (max-width: 567px) {
|
||||
.thought__heading {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="thought hue page">
|
||||
<div class="thought__form-container hue">
|
||||
<h2 id='title' class="thought__heading hue">Add a thought...</h2>
|
||||
<button class="thought__add say huet">
|
||||
<span aria-hidden="true">+</span>
|
||||
<span class="visually-hidden">Add thought</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="thought__list">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/gun.js"></script>
|
||||
<script>
|
||||
// Check out the interactive tutorial
|
||||
// for how to build a simplified version
|
||||
// of this example: https://scrimba.com/c/cW2Vsa
|
||||
var gun = Gun(location.origin + '/gun');
|
||||
var think = gun.get('think1/' + location.hash.slice(1));
|
||||
var thoughtItemStr = function(id) { return '<li class="thought__item"><label class="visually-hidden" for="' + id + '">Thought</label><input id="' + id + '" class="thought__input huet"><li/>'}
|
||||
var typing, throttle;
|
||||
$('.thought__add').on('click', function () {
|
||||
$(thoughtItemStr('')).prependTo('.thought__list').find('.thought__input').focus();
|
||||
});
|
||||
$(document).on('keyup', '.thought__input', function () {
|
||||
var input = $(this), id = input.attr('id');
|
||||
if (!id) {
|
||||
input.attr('id', id = String.random());
|
||||
}
|
||||
typing = id;
|
||||
clearTimeout(throttle);
|
||||
throttle = setTimeout(function () {
|
||||
think.get(id).put(input.val());
|
||||
typing = false;
|
||||
}, 10);
|
||||
});
|
||||
think.map().on(function (thought, id) {
|
||||
var li = $('#' + id).parent()[0] || $(thoughtItemStr(id)).prependTo('.thought__list');
|
||||
if (thought) {
|
||||
if (id === typing) { return }
|
||||
$(li).find('.thought__input').val(thought);
|
||||
} else {
|
||||
$(li).hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
|
77
examples/vanilla/screen.html
Normal file
77
examples/vanilla/screen.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Think</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<video id="video" width="100%"></video>
|
||||
<center>
|
||||
<button id="record">Record</button>
|
||||
<button id="play">Play</button>
|
||||
</center>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
|
||||
<script>
|
||||
const gun = Gun(`${window.location.origin}/gun`)
|
||||
const record = { recorder: null, recording: false }
|
||||
|
||||
const video = document.querySelector('#video')
|
||||
const playButton = document.querySelector('#play')
|
||||
const recordButton = document.querySelector('#record')
|
||||
|
||||
recordButton.addEventListener('click', () => {
|
||||
console.log(record)
|
||||
if (!record.ing) {
|
||||
return record.stream()
|
||||
}
|
||||
recordButton.innerText = 'Record'
|
||||
if (record.ing.stop) { record.ing.stop() }
|
||||
record.ing = false
|
||||
}, false)
|
||||
|
||||
record.stream = () => {
|
||||
navigator.mediaDevices.getDisplayMedia({ video: true }).then(stream => {
|
||||
const chunks = [] // we have a stream, we can record it
|
||||
record.ing = new MediaRecorder(stream)
|
||||
record.ing.ondataavailable = eve => chunks.push(eve.data)
|
||||
record.ing.onstop = () => record.save(new Blob(chunks))
|
||||
record.ing.start()
|
||||
recordButton.innerText = 'End'
|
||||
}, err => { console.log(err) })
|
||||
}
|
||||
|
||||
record.save = data => {
|
||||
record.file = record.file || new FileReader()
|
||||
record.file.readAsDataURL(data)
|
||||
record.file.onloadend = () => {
|
||||
let b64 = record.file.result
|
||||
b64 = `data:video/webm${b64.slice(b64.indexOf(';'))}`
|
||||
gun.get('test').get('screen').put(b64)
|
||||
}
|
||||
}
|
||||
|
||||
playButton.addEventListener('click', () => {
|
||||
if (record.playing) {
|
||||
playButton.innerText = 'Play'
|
||||
video.pause()
|
||||
record.playing = false
|
||||
return
|
||||
}
|
||||
|
||||
playButton.innerText = 'Stop'
|
||||
record.playing = true
|
||||
|
||||
gun.get('test').get('screen').once(data => {
|
||||
if (!data) { return }
|
||||
video.src = data
|
||||
video.play()
|
||||
})
|
||||
}, false)
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,53 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Think</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Thoughts</h1>
|
||||
<h1>Thoughts</h1>
|
||||
|
||||
<form id="form">
|
||||
<input id="input">
|
||||
<button>Add</button>
|
||||
</form>
|
||||
<form id="form">
|
||||
<input id="input">
|
||||
<button>Add</button>
|
||||
</form>
|
||||
|
||||
<ul id="parentList"></ul>
|
||||
<!-- <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<script>
|
||||
var gun = Gun().get('thoughts');
|
||||
document.getElementById('form').addEventListener('submit', function (e) {
|
||||
e.preventDefault(); // attaches event listener and prevent default form action
|
||||
var data = document.getElementById('input').value;
|
||||
gun.set(data);
|
||||
document.getElementById('input').value = "";
|
||||
});
|
||||
gun.map().on(function (thought, id) {
|
||||
var li = document.getElementById(id) || document.getElementById('parentList').insertAdjacentHTML('beforeend', '<li id =' + id + '> ' + thought + '</li>');
|
||||
var $ = function (selector) {
|
||||
return document.querySelector(selector);
|
||||
};
|
||||
// attach the event listener to the selected li items
|
||||
var links = $('#parentList').getElementsByTagName('li');
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var link = links[i];
|
||||
// console.log(link.innerHTML);
|
||||
link.ondblclick = dynamicEvent;
|
||||
if (link.innerHTML === " null" || link.innerHTML === " " || link.innerHTML === "") {
|
||||
link.style.display = "none";
|
||||
} else {
|
||||
link.style.display = "list-item";
|
||||
};
|
||||
};
|
||||
});
|
||||
function dynamicEvent() {
|
||||
gun.get(this.id).put(null);
|
||||
this.innerHTML = document.getElementById(this.id.innerHTML);
|
||||
if (this.innerHTML === " null" || this.innerHTML === " " || this.innerHTML === "") {
|
||||
this.style.display = "none";
|
||||
} else {
|
||||
this.style.display = "list-item";
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<ul id="parentList"></ul>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
|
||||
<script>
|
||||
const gun = Gun(`${window.location.origin}/gun`).get('thoughts')
|
||||
|
||||
const parentList = document.getElementById('parentList')
|
||||
const input = document.getElementById('input')
|
||||
const form = document.getElementById('form')
|
||||
|
||||
const dynamicEvent = e => {
|
||||
const target = e.target;
|
||||
|
||||
gun.get(target.id).put(null);
|
||||
|
||||
target.innerHTML = document.getElementById(target.id);
|
||||
|
||||
if (target.innerHTML === ' null' || target.innerHTML === ' ' || target.innerHTML === '') {
|
||||
target.style.display = 'none';
|
||||
} else {
|
||||
target.style.display = 'list-item';
|
||||
}
|
||||
}
|
||||
|
||||
gun.map().on((thought, id) => {
|
||||
parentList.insertAdjacentHTML('beforeend', `<li id =${id}> ${thought}</li>`);
|
||||
|
||||
const links = parentList.getElementsByTagName('li');
|
||||
|
||||
for (const link of links) {
|
||||
if (link.innerHTML === ' null' || link.innerHTML === ' ' || link.innerHTML === '') {
|
||||
link.style.display = 'none';
|
||||
} else {
|
||||
link.style.display = 'list-item';
|
||||
}
|
||||
link.ondblclick = dynamicEvent;
|
||||
}
|
||||
})
|
||||
|
||||
form.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
gun.set(input.value);
|
||||
input.value = '';
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
58
examples/vanilla/user.html
Normal file
58
examples/vanilla/user.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<h1>User</h1>
|
||||
|
||||
<form id="sign">
|
||||
<input id="alias" placeholder="username">
|
||||
<input id="pass" type="password" placeholder="passphrase">
|
||||
<input id="signup" type="button" value="sign up">
|
||||
<input id="signin" type="button" value="sign in">
|
||||
</form>
|
||||
|
||||
<ul id="list"></ul>
|
||||
|
||||
<form id="said">
|
||||
<input id="say">
|
||||
<input id="speak" type="submit" value="speak">
|
||||
</form>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
const gun = Gun(`${window.location.origin}/gun`)
|
||||
const user = gun.user().recall({ sessionStorage: true })
|
||||
|
||||
const alias = document.querySelector('#alias')
|
||||
const pass = document.querySelector('#pass')
|
||||
const sign = document.querySelector('#sign')
|
||||
const signup = document.querySelector('#signup')
|
||||
const signin = document.querySelector('#signin')
|
||||
const said = document.querySelector('#said')
|
||||
const say = document.querySelector('#say')
|
||||
const ul = document.querySelector('#list')
|
||||
|
||||
function UI (say, id) {
|
||||
const li = document.createElement('li')
|
||||
li.setAttribute('id', id)
|
||||
li.appendChild(document.createTextNode(say))
|
||||
ul.appendChild(li)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
signup.addEventListener('click', () => user.create(alias.value, pass.value, () => user.auth(alias.value, pass.value)))
|
||||
signin.addEventListener('click', () => user.auth(alias.value, pass.value))
|
||||
|
||||
said.addEventListener('submit', e => {
|
||||
e.preventDefault()
|
||||
// if(!user.is){ return }
|
||||
user.get('said').set(say.value)
|
||||
said.value = ''
|
||||
})
|
||||
|
||||
gun.on('auth', () => {
|
||||
sign.style.display = 'none'
|
||||
user.get('said').map().on(UI)
|
||||
})
|
||||
</script>
|
83
examples/vanilla/video.html
Normal file
83
examples/vanilla/video.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<video id="video" width="100%" controls autoplay></video>
|
||||
<center>
|
||||
<input id="pass" placeholder="password">
|
||||
Record <button class="record">Camera</button> or <button class="screen">Screen</button>
|
||||
</center>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
const peers = [
|
||||
`${window.location.origin}/gun`,
|
||||
'https://gun-us.herokuapp.com/gun',
|
||||
'https://gun-eu.herokuapp.com/gun'
|
||||
]
|
||||
|
||||
const gun = Gun({ localStorage: true, peers })
|
||||
|
||||
const pass = document.querySelector('#pass')
|
||||
const video = document.querySelector('#video')
|
||||
const camera = document.querySelector('.record')
|
||||
const screen = document.querySelector('.screen')
|
||||
|
||||
gun.get('test').get('video').on(async data => {
|
||||
if (pass.value) { data = await SEA.decrypt(data, pass.value) }
|
||||
video.setAttribute('src', data)
|
||||
})
|
||||
|
||||
function record(type) {
|
||||
function load(media) {
|
||||
const chunks = []
|
||||
record.ing = new MediaRecorder(media)
|
||||
record.ing.ondataavailable = eve => { chunks.push(eve.data) }
|
||||
record.ing.onstop = eve => { record.save(new Blob(chunks)) }
|
||||
record.ing.start()
|
||||
}
|
||||
|
||||
function error(err) { console.log(err) }
|
||||
|
||||
if (type === 'Camera') {
|
||||
navigator.getMedia({ video: true, audio: true }, load, error)
|
||||
}
|
||||
if (type === 'Screen') {
|
||||
navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then(load, error)
|
||||
}
|
||||
}
|
||||
|
||||
function act(e) {
|
||||
if (record.ing) {
|
||||
if (record.ing.stop) { record.ing.stop() }
|
||||
|
||||
e.target.textContent = record.type
|
||||
record.ing = false
|
||||
return
|
||||
}
|
||||
|
||||
record(record.type = e.target.textContent)
|
||||
e.target.textContent = 'end'
|
||||
}
|
||||
|
||||
record.save = data => {
|
||||
record.file = record.file || new FileReader()
|
||||
record.file.readAsDataURL(data)
|
||||
record.file.onloadend = async () => {
|
||||
const b64 = record.file.result
|
||||
let b64formated = `data:video/webm${b64.slice(b64.indexOf(';'))}`
|
||||
|
||||
video.setAttribute('src', b64formated)
|
||||
|
||||
if (pass.value) { b64formated = await SEA.encrypt(b64formated, pass.value) }
|
||||
gun.get('test').get('video').put(b64formated)
|
||||
}
|
||||
}
|
||||
|
||||
camera.addEventListener('click', e => act(e))
|
||||
screen.addEventListener('click', e => act(e))
|
||||
|
||||
navigator.getMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia || navigator.msGetUserMedia)
|
||||
|
||||
</script>
|
@ -9,7 +9,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
This is example of simple Vue plugin. It works exatcly same as the Vue instance data property, but the name is gunData.<br>
|
||||
This is example of simple Vue plugin. It works exactly same as the Vue instance data property, but the name is gunData.<br>
|
||||
The cool part is that every property in the gunData is realtime synced via gunDB to every other page viewer!<br>
|
||||
<table>
|
||||
<tr>
|
||||
|
40
examples/wave.html
Normal file
40
examples/wave.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Wave example</title>
|
||||
</head>
|
||||
<body>
|
||||
<button id="bPiano">Piano left</button>
|
||||
<button id="bGuitar">Guitar itched</button>
|
||||
<button id="bXylo">Xylophone slow</button>
|
||||
<button id="bAll">All in one</button>
|
||||
|
||||
<script src="../lib/wave.js"></script>
|
||||
<script>
|
||||
function piano() {
|
||||
wave(':piano:zxcvbn').balance(-1).play()
|
||||
}
|
||||
function guitar() {
|
||||
wave(':guitar:zxcvbn').itch(0.5).play()
|
||||
}
|
||||
function xylo() {
|
||||
wave(':xylophone:zxcvbn').pace(100).play()
|
||||
}
|
||||
function all() {
|
||||
wave(`
|
||||
:pace 400: :itch 0: :balance 0:
|
||||
|
||||
:piano: :balance -1: zxcvbn
|
||||
|
||||
:guitar: :balance 0: :itch 0.5: zxcvbn
|
||||
|
||||
:xylophone: :itch 0: :pace 100: zxcvbn
|
||||
`).play()
|
||||
}
|
||||
document.querySelector('#bPiano').addEventListener("click", piano)
|
||||
document.querySelector('#bGuitar').addEventListener("click", guitar)
|
||||
document.querySelector('#bXylo').addEventListener("click", xylo)
|
||||
document.querySelector('#bAll').addEventListener("click", all)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
3
examples/webpack/.gitignore
vendored
Normal file
3
examples/webpack/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
package-lock.json
|
||||
node_modules
|
||||
public
|
17
examples/webpack/package.json
Normal file
17
examples/webpack/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "webpack",
|
||||
"version": "1.0.0",
|
||||
"description": "webpack build example",
|
||||
"scripts": {
|
||||
"build": "webpack --devtool source-map --config webpack.config.js",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"gun": "github:amark/gun",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"webpack": "^5.72.1",
|
||||
"webpack-cli": "^4.9.2"
|
||||
}
|
||||
}
|
9
examples/webpack/src/app.js
Normal file
9
examples/webpack/src/app.js
Normal file
@ -0,0 +1,9 @@
|
||||
define(function(require, exports, module) {
|
||||
|
||||
var Gun = require("gun");
|
||||
|
||||
var gun = new Gun();
|
||||
|
||||
gun.get("hello").get("world").put("from gun").on((data, key) => console.log(data, key));
|
||||
|
||||
});
|
5
examples/webpack/src/index.html
Normal file
5
examples/webpack/src/index.html
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body></body>
|
||||
</html>
|
35
examples/webpack/webpack.config.js
Normal file
35
examples/webpack/webpack.config.js
Normal file
@ -0,0 +1,35 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
let plugins = [];
|
||||
|
||||
plugins.push(new HtmlWebpackPlugin({
|
||||
filename: './index.html',
|
||||
template: './src/index.html',
|
||||
inject: true,
|
||||
minify: false,
|
||||
hash: false,
|
||||
cache: false,
|
||||
showErrors: false
|
||||
}));
|
||||
|
||||
console.log("webpack config loaded");
|
||||
|
||||
module.exports = {
|
||||
|
||||
mode: "development",
|
||||
|
||||
// stats: 'minimal',
|
||||
stats: 'normal',
|
||||
// stats: 'verbose',
|
||||
|
||||
entry: [path.resolve(__dirname, './src/app.js')],
|
||||
|
||||
output: {
|
||||
path: path.resolve(__dirname, './public'),
|
||||
clean: true,
|
||||
filename: './app.js'
|
||||
},
|
||||
|
||||
plugins: plugins
|
||||
};
|
23
gun.d.ts
vendored
Normal file
23
gun.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
import { IGun, LEX } from './types/gun';
|
||||
|
||||
declare const Gun: IGun;
|
||||
export default Gun;
|
||||
|
||||
import {} from './types/gun/IGun';
|
||||
declare module './types/gun/IGun' {
|
||||
export interface IGun {
|
||||
window: Window
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Gun: IGun;
|
||||
GUN: IGun;
|
||||
}
|
||||
|
||||
interface StringConstructor {
|
||||
match(t: string, o: LEX | string): boolean;
|
||||
random(length?: number, alphabet?: string): string;
|
||||
}
|
||||
}
|
3
gun.min.js
vendored
3
gun.min.js
vendored
File diff suppressed because one or more lines are too long
19
index.d.ts
vendored
Normal file
19
index.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
export * from './types/gun';
|
||||
export * from './types/sea';
|
||||
|
||||
import { IGun, LEX } from './types/gun';
|
||||
import { ISEA } from './types/sea';
|
||||
|
||||
declare const Gun: IGun;
|
||||
export default Gun;
|
||||
|
||||
export const SEA: ISEA;
|
||||
|
||||
declare global {
|
||||
const Gun: IGun;
|
||||
|
||||
interface StringConstructor {
|
||||
match(t: string, o: LEX | string): boolean;
|
||||
random(length?: number, alphabet?: string): string;
|
||||
}
|
||||
}
|
13
lib/afore.js
Normal file
13
lib/afore.js
Normal file
@ -0,0 +1,13 @@
|
||||
function afore(tag, hear){
|
||||
if(!tag){ return }
|
||||
tag = tag.the; // grab the linked list root
|
||||
var tmp = tag.to; // grab first listener
|
||||
hear = tmp.on.on(tag.tag, hear); // add us to end
|
||||
hear.to = tmp || hear.to; // make our next be current first
|
||||
hear.back.to = hear.to; // make our back point to our next
|
||||
tag.last = hear.back; // make last be same as before
|
||||
hear.back = tag; // make our back be the start
|
||||
tag.to = hear; // make the start be us
|
||||
return hear;
|
||||
}
|
||||
if(typeof module !== "undefined"){ module.exports = afore } // afore(gun._.on('in'), function(){ })
|
@ -7,7 +7,7 @@
|
||||
var s = this;
|
||||
opt = opt || {};
|
||||
opt.bucket = opt.bucket || opt.Bucket || process.env.AWS_S3_BUCKET;
|
||||
opt.region = opt.region || process.AWS_REGION || "us-east-1";
|
||||
opt.region = opt.region || process.env.AWS_REGION || "us-east-1";
|
||||
opt.accessKeyId = opt.key = opt.key || opt.accessKeyId || process.env.AWS_ACCESS_KEY_ID;
|
||||
opt.secretAccessKey = opt.secret = opt.secret || opt.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
|
||||
if(!opt.accessKeyId || !opt.secretAccessKey){
|
||||
|
283
lib/axe.js
Normal file
283
lib/axe.js
Normal file
@ -0,0 +1,283 @@
|
||||
// I don't quite know where this should go yet, so putting it here
|
||||
// what will probably wind up happening is that minimal AXE logic added to end of gun.js
|
||||
// and then rest of AXE logic (here) will be moved back to gun/axe.js
|
||||
// but for now... I gotta rush this out!
|
||||
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'), u;
|
||||
Gun.on('opt', function(at){ start(at); this.to.next(at) }); // make sure to call the "next" middleware adapter.
|
||||
// TODO: BUG: panic test/panic/1 & test/panic/3 fail when AXE is on.
|
||||
function start(root){
|
||||
if(root.axe){ return }
|
||||
var opt = root.opt, peers = opt.peers;
|
||||
if(false === opt.axe){ return }
|
||||
if((typeof process !== "undefined") && 'false' === ''+(opt.env=process.env||'').AXE){ return }
|
||||
Gun.log.once("AXE", "AXE relay enabled!");
|
||||
var axe = root.axe = {}, tmp, id;
|
||||
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root); // DAM!
|
||||
var dup = root.dup;
|
||||
|
||||
mesh.way = function(msg){
|
||||
if(!msg){ return }
|
||||
//relayUp(msg); // TEMPORARY!!!
|
||||
if(msg.get){ return GET(msg) }
|
||||
if(msg.put){ return }
|
||||
fall(msg);
|
||||
}
|
||||
|
||||
function GET(msg){
|
||||
if(!msg){ return }
|
||||
var via = (msg._||'').via, soul, has, tmp, ref;
|
||||
if(!via || !via.id){ return fall(msg) }
|
||||
// SUBSCRIPTION LOGIC MOVED TO GET'S ACK REPLY.
|
||||
if(!(ref = REF(msg)._)){ return fall(msg) }
|
||||
ref.asked = +new Date;
|
||||
GET.turn(msg, ref.route, 0);
|
||||
}
|
||||
GET.turn = function(msg, route, turn){
|
||||
var tmp = msg['#'], tag = dup.s[tmp], next;
|
||||
if(!tmp || !tag){ return } // message timed out, GUN may require us to relay, tho AXE does not like that. Rethink?
|
||||
// TOOD: BUG! Handle edge case where live updates occur while these turn hashes are being checked (they'll never be consistent), but we don't want to degrade to O(N), if we know the via asking peer got an update, then we should do something like cancel these turns asking for data.
|
||||
// Ideas: Save a random seed that sorts the route, store it and the index. // Or indexing on lowest latency is probably better.
|
||||
clearTimeout(tag.lack);
|
||||
if(tag.ack && (tmp = tag['##']) && msg['##'] === tmp){ return } // hashes match, stop asking other peers!
|
||||
next = (Object.maps(route||opt.peers)).slice(turn = turn || 0);
|
||||
if(!next.length){
|
||||
if(!route){ return } // asked all peers, stop asking!
|
||||
GET.turn(msg, u, 0); // asked all subs, now now ask any peers. (not always the best idea, but stays )
|
||||
return;
|
||||
}
|
||||
setTimeout.each(next, function(id){
|
||||
var peer = opt.peers[id]; turn++;
|
||||
if(!peer || !peer.wire){ route && route.delete(id); return } // bye! // TODO: CHECK IF 0 OTHER PEERS & UNSUBSCRIBE
|
||||
if(mesh.say(msg, peer) === false){ return } // was self
|
||||
if(0 == (turn % 3)){ return 1 }
|
||||
}, function(){
|
||||
tag['##'] = msg['##']; // should probably set this in a more clever manner, do live `in` checks ++ --, etc. but being lazy for now. // TODO: Yes, see `in` TODO, currently this might match against only in-mem cause no other peers reply, which is "fine", but could cause a false positive.
|
||||
tag.lack = setTimeout(function(){ GET.turn(msg, route, turn) }, 25);
|
||||
}, 3);
|
||||
}
|
||||
function fall(msg){ mesh.say(msg, opt.peers) }
|
||||
function REF(msg){
|
||||
var ref = '', soul, has, tmp;
|
||||
if(!msg || !msg.get){ return ref }
|
||||
if('string' == typeof (soul = msg.get['#'])){ ref = root.$.get(soul) }
|
||||
if('string' == typeof (tmp = msg.get['.'])){ has = tmp } else { has = '' }
|
||||
|
||||
var via = (msg._||'').via, sub = (via.sub || (via.sub = new Object.Map)); (sub.get(soul) || (sub.set(soul, tmp = new Object.Map) && tmp)).set(has, 1); // {soul: {'':1, has: 1}} // TEMPORARILY REVERT AXE TOWER TYING TO SUBSCRIBING TO EVERYTHING. UNDO THIS!
|
||||
via.id && ref._ && (ref._.route || (ref._.route = new Object.Map)).set(via.id, via); // SAME AS ^
|
||||
|
||||
return ref;
|
||||
}
|
||||
function LEX(lex){ return (lex = lex || '')['='] || lex['*'] || lex['>'] || lex }
|
||||
|
||||
root.on('in', function(msg){ var to = this.to, tmp;
|
||||
if((tmp = msg['@']) && (tmp = dup.s[tmp])){
|
||||
tmp.ack = (tmp.ack || 0) + 1; // count remote ACKs to GET. // TODO: If mismatch, should trigger next asks.
|
||||
if(tmp.it && tmp.it.get && msg.put){ // WHEN SEEING A PUT REPLY TO A GET...
|
||||
var get = tmp.it.get||'', ref = REF(tmp.it)._, via = (tmp.it._||'').via||'', sub;
|
||||
if(via && ref){ // SUBSCRIBE THE PEER WHO ASKED VIA FOR IT:
|
||||
//console.log("SUBSCRIBING", Object.maps(ref.route||''), "to", LEX(get['#']));
|
||||
via.id && (ref.route || (ref.route = new Object.Map)).set(via.id, via);
|
||||
sub = (via.sub || (via.sub = new Object.Map));
|
||||
ref && (sub.get(LEX(get['#'])) || (sub.set(LEX(get['#']), sub = new Object.Map) && sub)).set(LEX(get['.']), 1); // {soul: {'':1, has: 1}}
|
||||
|
||||
via = (msg._||'').via||'';
|
||||
if(via){ // BIDIRECTIONAL SUBSCRIBE: REPLIER IS NOW SUBSCRIBED. DO WE WANT THIS?
|
||||
via.id && (ref.route || (ref.route = new Object.Map)).set(via.id, via);
|
||||
sub = (via.sub || (via.sub = new Object.Map));
|
||||
if(ref){
|
||||
var soul = LEX(get['#']), sift = sub.get(soul), has = LEX(get['.']);
|
||||
if(has){
|
||||
(sift || (sub.set(soul, sift = new Object.Map) && sift)).set(has, 1);
|
||||
} else
|
||||
if(!sift){
|
||||
sub.set(soul, sift = new Object.Map);
|
||||
sift.set('', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if((tmp = tmp.back)){ // backtrack OKs since AXE splits PUTs up.
|
||||
setTimeout.each(Object.keys(tmp), function(id){
|
||||
to.next({'#': msg['#'], '@': id, ok: msg.ok});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
to.next(msg);
|
||||
});
|
||||
|
||||
root.on('create', function(root){
|
||||
this.to.next(root);
|
||||
var Q = {};
|
||||
root.on('put', function(msg){
|
||||
var eve = this, at = eve.as, put = msg.put, soul = put['#'], has = put['.'], val = put[':'], state = put['>'], q, tmp;
|
||||
eve.to.next(msg);
|
||||
if(msg['@']){ return } // acks send existing data, not updates, so no need to resend to others.
|
||||
if(!soul || !has){ return }
|
||||
var ref = root.$.get(soul)._, route = (ref||'').route;
|
||||
if(!route){ return }
|
||||
if(ref.skip && ref.skip.has == has){ ref.skip.now = msg['#']; return }
|
||||
(ref.skip = {now: msg['#'], has: has}).to = setTimeout(function(){
|
||||
setTimeout.each(Object.maps(route), function(pid){ var peer, tmp;
|
||||
var skip = ref.skip||''; ref.skip = null;
|
||||
if(!(peer = route.get(pid))){ return }
|
||||
if(!peer.wire){ route.delete(pid); return } // bye!
|
||||
var sub = (peer.sub || (peer.sub = new Object.Map)).get(soul);
|
||||
if(!sub){ return }
|
||||
if(!sub.get(has) && !sub.get('')){ return }
|
||||
var put = peer.put || (peer.put = {});
|
||||
var node = root.graph[soul], tmp;
|
||||
if(node && u !== (tmp = node[has])){
|
||||
state = state_is(node, has);
|
||||
val = tmp;
|
||||
}
|
||||
put[soul] = state_ify(put[soul], has, state, val, soul);
|
||||
tmp = dup.track(peer.next = peer.next || String.random(9));
|
||||
(tmp.back || (tmp.back = {}))[''+(skip.now||msg['#'])] = 1;
|
||||
if(peer.to){ return }
|
||||
peer.to = setTimeout(function(){ flush(peer) }, opt.gap);
|
||||
}) }, 9);
|
||||
});
|
||||
});
|
||||
|
||||
function flush(peer){
|
||||
var msg = {'#': peer.next, put: peer.put, ok: {'@': 3, '/': mesh.near}}; // BUG: TODO: sub count!
|
||||
// TODO: what about DAM's >< dedup? Current thinking is, don't use it, however, you could store first msg# & latest msg#, and if here... latest === first then likely it is the same >< thing, so if(firstMsg['><'][peer.id]){ return } don't send.
|
||||
peer.next = peer.put = peer.to = null;
|
||||
mesh.say(msg, peer);
|
||||
}
|
||||
var state_ify = Gun.state.ify, state_is = Gun.state.is;
|
||||
|
||||
function relayUp(msg){
|
||||
mesh.say(msg, axe.up);
|
||||
}
|
||||
|
||||
;(function(){ // THIS IS THE UP MODULE;
|
||||
axe.up = {};
|
||||
var hi = mesh.hear['?']; // lower-level integration with DAM! This is abnormal but helps performance.
|
||||
mesh.hear['?'] = function(msg, peer){ var p; // deduplicate unnecessary connections:
|
||||
hi(msg, peer);
|
||||
if(!peer.pid){ return }
|
||||
if(peer.pid === opt.pid){ mesh.bye(peer); return } // if I connected to myself, drop.
|
||||
if(p = axe.up[peer.pid]){ // if we both connected to each other...
|
||||
if(p === peer){ return } // do nothing if no conflict,
|
||||
if(opt.pid > peer.pid){ // else deterministically sort
|
||||
p = peer; // so we will wind up choosing the same to keep
|
||||
peer = axe.up[p.pid]; // and the same to drop.
|
||||
}
|
||||
p.url = p.url || peer.url; // copy if not
|
||||
mesh.bye(peer); // drop
|
||||
axe.up[p.pid] = p; // update same to be same.
|
||||
return;
|
||||
}
|
||||
if(!peer.url){ return }
|
||||
axe.up[peer.pid] = peer;
|
||||
if(axe.stay){ axe.stay() }
|
||||
};
|
||||
|
||||
mesh.hear['opt'] = function(msg, peer){
|
||||
if(msg.ok){ return }
|
||||
var tmp = msg.opt;
|
||||
if(!tmp){ return }
|
||||
tmp = tmp.peers;
|
||||
if(!tmp || 'string' != typeof tmp){ return }
|
||||
if(99 <= Object.keys(axe.up).length){ return } // 99 TEMPORARILY UNTIL BENCHMARKED!
|
||||
mesh.hi({id: tmp, url: tmp, retry: 9});
|
||||
if(peer){ mesh.say({dam: 'opt', ok: 1, '@': msg['#']}, peer) }
|
||||
}
|
||||
|
||||
axe.stay = function(){
|
||||
clearTimeout(axe.stay.to);
|
||||
axe.stay.to = setTimeout(function(tmp, urls){
|
||||
if(!(tmp = root.stats && root.stats.stay)){ return }
|
||||
urls = {}; Object.keys(axe.up||'').forEach(function(p){
|
||||
p = (axe.up||'')[p]; if(p.url){ urls[p.url] = {} }
|
||||
});
|
||||
(tmp.axe = tmp.axe || {}).up = urls;
|
||||
}, 1000 * 9);//1000 * 60);
|
||||
};
|
||||
setTimeout(function(tmp){
|
||||
if(!(tmp = root.stats && root.stats.stay && root.stats.stay.axe)){ return }
|
||||
if(!(tmp = tmp.up)){ return }
|
||||
if(!(tmp instanceof Array)){ tmp = Object.keys(tmp) }
|
||||
setTimeout.each(tmp||[], function(url){ mesh.hear.opt({opt: {peers: url}}) });
|
||||
},1000);
|
||||
}());
|
||||
|
||||
setTimeout(function(){ require('./service')(root) },9);
|
||||
|
||||
;(function(){ // THIS IS THE MOB MODULE;
|
||||
//return; // WORK IN PROGRESS, TEST FINALIZED, NEED TO MAKE STABLE.
|
||||
/*
|
||||
AXE should have a couple of threshold items...
|
||||
let's pretend there is a variable max peers connected
|
||||
mob = 10000
|
||||
if we get more peers than that...
|
||||
we should start sending those peers a remote command
|
||||
that they should connect to this or that other peer
|
||||
and then once they (or before they do?) drop them from us.
|
||||
sake of the test... gonna set that peer number to 1.
|
||||
The mob threshold might be determined by other factors,
|
||||
like how much RAM or CPU stress we have.
|
||||
*/
|
||||
opt.mob = opt.mob || parseFloat((opt.env||'').MOB) || 999999; // should be based on ulimit, some clouds as low as 10K.
|
||||
|
||||
// handle rebalancing a mob of peers:
|
||||
root.on('hi', function(peer){
|
||||
this.to.next(peer);
|
||||
if(peer.url){ return } // I am assuming that if we are wanting to make an outbound connection to them, that we don't ever want to drop them unless our actual config settings change.
|
||||
var count = /*Object.keys(opt.peers).length ||*/ mesh.near; // TODO: BUG! This is slow, use .near, but near is buggy right now, fix in DAM.
|
||||
//console.log("are we mobbed?", opt.mob, Object.keys(opt.peers).length, mesh.near);
|
||||
if(opt.mob >= count){ return } // TODO: Make dynamic based on RAM/CPU also. Or possibly even weird stuff like opt.mob / axe.up length?
|
||||
var peers = {};Object.keys(axe.up).forEach(function(p){ p = axe.up[p]; p.url && (peers[p.url]={}) });
|
||||
// TODO: BUG!!! Infinite reconnection loop happens if not enough relays, or if some are missing. For instance, :8766 says to connect to :8767 which then says to connect to :8766. To not DDoS when system overload, figure clever way to tell peers to retry later, that network does not have enough capacity?
|
||||
mesh.say({dam: 'mob', mob: count, peers: peers}, peer);
|
||||
setTimeout(function(){ mesh.bye(peer) }, 9); // something with better perf?
|
||||
});
|
||||
root.on('bye', function(peer){
|
||||
this.to.next(peer);
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
;(function(){ // THIS IS THE UNIVERSAL NOTIFICATION MODULE
|
||||
var to = {}, key = {}, email = require('./email');
|
||||
if(email.err){ return }
|
||||
mesh.hear['tag'] = function(msg, peer, who){
|
||||
if(who = key[msg.key]){ who.rate = Math.max(msg.rate||1000*60*15, 1000*60); return }
|
||||
if(!msg.src || !msg.email){ return }
|
||||
if(+new Date < peer.emailed + 1000*60*2){ mesh.say({dam:'tag',err:'too fast'},peer); return } // peer can only send notifications > 2min
|
||||
var src; try{ src = new URL(msg.src = msg.src.split(/\s/)[0]); } catch(e){ return } // throws if invalid URL.
|
||||
(who = (to[msg.email] = to[msg.email] || {go:{}})).go[''+src] = 1; // we're keeping in-memory for now, maybe will "stay" to disk in future.
|
||||
peer.emailed = +new Date;
|
||||
if(who.batch){ return }
|
||||
key[who.key = Math.random().toString(36).slice(2)] = who;
|
||||
who.batch = setTimeout(function(){
|
||||
email.send({
|
||||
from: process.env.EMAIL,
|
||||
to: msg.email,
|
||||
subject: "Notification:",
|
||||
text: 'Someone or a bot tagged you at: (⚠️ only click link if you recognize & trust it ⚠️)\n'+
|
||||
'[use #'+who.key+' to unsubscribe please mute this thread by tapping the top most "⋮" button and clicking mute]\n\n' +
|
||||
Object.keys(who.go).join('\n'), // TODO: NEEDS TO BE CPU SCHEDULED
|
||||
headers: {'message-id': '<123456789.8765@example.com>'} // hardcode id so all batches also group into the same email thread to reduce clutter.
|
||||
}, function(err, r){
|
||||
who.batch = null; who.go = {};
|
||||
err && console.log("email TAG:", err);
|
||||
});
|
||||
}, who.rate || (1000*60*60*24)); // default to 1 day
|
||||
};
|
||||
}());
|
||||
};
|
||||
|
||||
;(function(){
|
||||
var from = Array.from;
|
||||
Object.maps = function(o){
|
||||
if(from && o instanceof Map){ return from(o.keys()) }
|
||||
if(o instanceof Object.Map){ o = o.s }
|
||||
return Object.keys(o);
|
||||
}
|
||||
if(from){ return Object.Map = Map }
|
||||
(Object.Map = function(){ this.s = {} }).prototype = {set:function(k,v){this.s[k]=v;return this},get:function(k){return this.s[k]},delete:function(k){delete this.s[k]}};
|
||||
}());
|
1
lib/book.js
Normal file
1
lib/book.js
Normal file
@ -0,0 +1 @@
|
||||
console.log("Officially moved to gun.js core, use gun/src/book.js on own.");
|
24
lib/bye.js
24
lib/bye.js
@ -1,24 +1,22 @@
|
||||
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
|
||||
|
||||
Gun.on('opt', function(root){
|
||||
Gun.on('create', function(root){
|
||||
this.to.next(root);
|
||||
if(root.once){ return }
|
||||
root.on('in', function(msg){
|
||||
//Msg did not have a peer property saved before, so nothing ever went further
|
||||
if(!msg.mesh || !msg.BYE){ return this.to.next(msg) }
|
||||
var peer = msg.mesh.via;
|
||||
(peer.bye = peer.bye || []).push(msg.BYE);
|
||||
})
|
||||
var mesh = root.opt.mesh;
|
||||
if(!mesh){ return }
|
||||
mesh.hear['bye'] = function(msg, peer){
|
||||
(peer.byes = peer.byes || []).push(msg.bye);
|
||||
}
|
||||
root.on('bye', function(peer){
|
||||
this.to.next(peer);
|
||||
if(!peer.bye){ return }
|
||||
var gun = root.gun;
|
||||
Gun.obj.map(peer.bye, function(data){
|
||||
if(!peer.byes){ return }
|
||||
var gun = root.$;
|
||||
Gun.obj.map(peer.byes, function(data){
|
||||
Gun.obj.map(data, function(put, soul){
|
||||
gun.get(soul).put(put);
|
||||
});
|
||||
});
|
||||
peer.bye = [];
|
||||
peer.byes = [];
|
||||
});
|
||||
});
|
||||
|
||||
@ -30,7 +28,7 @@ Gun.chain.bye = function(){
|
||||
var tmp = data;
|
||||
(data = {})[at.get] = tmp;
|
||||
});
|
||||
root.on('out', {BYE: data});
|
||||
root.on('out', {bye: data});
|
||||
return gun;
|
||||
}
|
||||
return bye;
|
||||
|
32
lib/crashed.js
Normal file
32
lib/crashed.js
Normal file
@ -0,0 +1,32 @@
|
||||
;(function(){ try {
|
||||
var fs = require('fs'), logs = [], up = __dirname+'/../';
|
||||
fs.readdir(up, function(err, list){ try{
|
||||
var i = 0, f; while(f = list[i++]){
|
||||
if(0 === f.indexOf('isolate-') && '.log' === f.slice(-4)){ logs.push(f) }
|
||||
}
|
||||
logs = logs.sort();
|
||||
var i = 0, f, lf; while(f = list[i++]){
|
||||
if(0 <= f.indexOf('-v8-') && '.log' === f.slice(-4)){ lf = f }
|
||||
} f = lf;
|
||||
if(!f){ return }
|
||||
fs.rename(up+f, up+'v8.log', function(err,ok){
|
||||
var i = 0, f; while(f = logs[i++]){ fs.unlink(up+f, noop) }
|
||||
if(!process.env.EMAIL){ return } // ONLY EMAIL IF DEVELOPER OPTS IN!!!
|
||||
email(); // ONLY EMAIL IF DEVELOPER OPTS IN!!!
|
||||
});
|
||||
}catch(e){} });
|
||||
function noop(){};
|
||||
function email(){ try{
|
||||
if(!process.env.EMAIL){ return } // ONLY EMAIL IF DEVELOPER OPTS IN!!!
|
||||
var address = process.env.EMAIL || "mark@gun.eco";
|
||||
// you also have to specify your EMAIL_KEY gmail 2F' app's password (not reg) to send out.
|
||||
require('./email').send({
|
||||
text: "log attached",
|
||||
from: address,
|
||||
to: address,
|
||||
subject: "GUN V8 LOG",
|
||||
attachment:[{path: up+'v8.log', type:"text/plain", name:"v8.log"}]
|
||||
}, noop);
|
||||
}catch(e){} };
|
||||
}catch(e){}
|
||||
}());
|
75
lib/dom.js
Normal file
75
lib/dom.js
Normal file
@ -0,0 +1,75 @@
|
||||
;(function(){ // jQuery shim
|
||||
// u = undefined, n = null, b = boolean = true/false, n = number, t = text, l = list = array, o = object, cb = callback = function, q = query CSS, k = key, eve = event.
|
||||
if(window.$){ return }
|
||||
(($ = window.$ = function(q, tag, I, u){
|
||||
if(q instanceof $){ return q }
|
||||
if(!((I = this) instanceof $)){ return new $(q, tag) }
|
||||
if('string' != typeof q){ return I.tags = (q = q||[]).tags || (u === q.length)? [q] : q, I }
|
||||
if('<' === q[0]){ return I.add(q) }
|
||||
return q.split(",").forEach(function(q){ I.add((tag||document).querySelectorAll(q)) }), I;
|
||||
}).fn = $.prototype).each = function(cb){ return $.each(this.tags, cb), this }
|
||||
$.each = function(o, cb){ Object.keys(o).forEach(function(k){ cb(k, o[k]) }) }
|
||||
$.isPlainObject = function(o){
|
||||
return (o? (o instanceof Object && o.constructor === Object)
|
||||
|| 'Object' === Object.prototype.toString.call(o).match(/^\[object (\w+)\]$/)[1]
|
||||
: false);
|
||||
}
|
||||
$.fn.add = function(add, tmp, u){ if(!add){ return this }
|
||||
if('<' === (tmp = add)[0]){ (add = document.createElement('div')).innerHTML = tmp; add = add.children[0] }
|
||||
add = ('string' == typeof add)? $(add).tags : (u == add.length)? add : [].slice.call(add);
|
||||
return this.tags = [].slice.call(this.tags||[]).concat(add), this;
|
||||
}
|
||||
$.fn.get = function(i, l, u){ return l = this.tags, (i === u)? l : l[i] }
|
||||
$.fn.is = function(q, b){ return this.each(function(i, tag){ b = b || tag.matches(q) }), b }
|
||||
$.fn.css = function(o){ return this.each(function(i, tag){ $.each(o, function(k,v){ tag.style[k] = v }) })}
|
||||
$.fn.on = function(t, cb){ return this.each(function(i, tag){
|
||||
t.split(" ").forEach(function(t){ tag.addEventListener(t, cb) });
|
||||
})}
|
||||
$.fn.val = function(t, k, f, u){
|
||||
t = (t === u)? '' : (f = 1) && t;
|
||||
k = k || 'value';
|
||||
return this.each(function(i, tag){
|
||||
if(f){ tag[k] = t }
|
||||
else { t += (tag[k]||'') }
|
||||
}), f? this : t;
|
||||
}
|
||||
$.fn.text = function(t){ return this.val(t, 'textContent') }
|
||||
$.fn.html = function(html){ return this.val(html, 'innerHTML') }
|
||||
$.fn.attr = function(attr,val){ return this.val(val, attr) }
|
||||
$.fn.find = function(q, I, l){
|
||||
I = $(), l = I.tags;
|
||||
return this.each(function(i, tag){
|
||||
$(q, tag).each(function(i, tag){
|
||||
if(0 > l.indexOf(tag)){ l.push(tag) }
|
||||
});
|
||||
}), I;
|
||||
}
|
||||
$.fn.place = function(where, on, f, op, I){ return (I = this).each(function(i, tag){ $(on).each(function(i, node){
|
||||
(f? tag : node)[op||'insertAdjacentElement'](({
|
||||
'-1':'beforebegin', '-0.1': 'afterbegin', '0.1':'beforeend', '1': 'afterend'
|
||||
})[where], (f? node : tag));
|
||||
})})}
|
||||
$.fn.append = function(html){ return $(html).place(0.1, this), this }
|
||||
$.fn.appendTo = function(html){ return this.place(0.1, $(html)) }
|
||||
function rev(o, I){ (I = $()).tags = [].slice.call(o.tags).reverse(); return I };
|
||||
$.fn.prependTo = function(html){ return rev(this).place(-0.1, $(html)), this }
|
||||
$.fn.prepend = function(html){ return rev($(html)).place(-0.1, this), this }
|
||||
$.fn.parents = function(q, c, I, l, p){
|
||||
I = $(), l = I.tags, p = 'parentElement';
|
||||
this.each(function(i, tag){
|
||||
if(c){ (c = {})[p] = tag ; tag = c }
|
||||
while(tag){ if((tag = tag[p]) && $(tag).is(q)){
|
||||
l.push(tag); if(c){ return }
|
||||
}}
|
||||
});
|
||||
return I;
|
||||
}
|
||||
$.fn.closest = function(q, c){ return this.parents(q, 1) }
|
||||
$.fn.clone = function(b, I, l){
|
||||
I = $(), l = I.tags;
|
||||
this.each(function(i, tag){
|
||||
l.push(tag.cloneNode(true))
|
||||
});
|
||||
return I;
|
||||
}
|
||||
}());
|
12
lib/email.js
Normal file
12
lib/email.js
Normal file
@ -0,0 +1,12 @@
|
||||
;(function(){
|
||||
var email, fail = {send: function(opt, cb){ cb && cb("You do not have email installed.") } };
|
||||
if(!process.env.EMAIL){ return module.exports = fail }
|
||||
try{ email = require('emailjs') }catch(e){};
|
||||
if(!email){ return module.exports = fail }
|
||||
return module.exports = email.server.connect({
|
||||
user: process.env.EMAIL,
|
||||
password: process.env.EMAIL_KEY,
|
||||
host: process.env.EMAIL_HOST || "smtp.gmail.com",
|
||||
ssl: process.env.EMAIL_SSL || true
|
||||
});
|
||||
}());
|
31
lib/evict.js
31
lib/evict.js
@ -5,26 +5,33 @@
|
||||
this.to.next(root);
|
||||
if(root.once){ return }
|
||||
if(typeof process == 'undefined'){ return }
|
||||
var util = process.memoryUsage;
|
||||
var util = process.memoryUsage, heap;
|
||||
if(!util){ return }
|
||||
|
||||
ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though.
|
||||
try{ heap = require('v8').getHeapStatistics }catch(e){}
|
||||
if(!heap){ return }
|
||||
|
||||
ev.max = parseFloat(root.opt.memory || (heap().heap_size_limit / 1024 / 1024) || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though. // KEEPING USED_HEA_SIZE < HEAP_SIZE_LIMIT ONLY THING TO BE BELOW TO PREVENT CRASH!
|
||||
|
||||
setInterval(check, 1000);
|
||||
function check(){
|
||||
var used = ev.used = util().rss / 1024 / 1024;
|
||||
if(used < ev.max){ return }
|
||||
setTimeout(GC, 1);
|
||||
var used = util().rss / 1024 / 1024;
|
||||
var hused = heap().used_heap_size / 1024 / 1024;
|
||||
var tmp; if(tmp = console.STAT){ tmp.memax = parseFloat(ev.max.toFixed(1)); tmp.memused = parseFloat(used.toFixed(1)); tmp.memhused = parseFloat(hused.toFixed(1)); }
|
||||
if(hused < ev.max && used < ev.max){ return }
|
||||
//if(used < ev.max){ return }
|
||||
console.STAT && console.STAT('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed());
|
||||
GC();//setTimeout(GC, 1);
|
||||
}
|
||||
function GC(){
|
||||
var S = +new Date;
|
||||
var souls = Object.keys(root.graph||empty);
|
||||
var toss = Math.ceil(souls.length * 0.01);
|
||||
//var start = Gun.state(), i = toss;
|
||||
Gun.list.map(souls, function(soul){
|
||||
if(--toss < 0){ return }
|
||||
root.gun.get(soul).off();
|
||||
});
|
||||
//console.log("evicted", i, 'nodes in', ((Gun.state() - start)/1000).toFixed(2), 'sec.');
|
||||
setTimeout.each(souls, function(soul){
|
||||
if(--toss < 0){ return 1 }
|
||||
root.$.get(soul).off();
|
||||
},0,99);
|
||||
root.dup.drop(1000 * 9); // clean up message tracker
|
||||
console.STAT && console.STAT(S, +new Date - S, 'evict');
|
||||
}
|
||||
/*
|
||||
root.on('in', function(msg){
|
||||
|
81
lib/fork.js
Normal file
81
lib/fork.js
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
describe('API Chain Features', function(){
|
||||
|
||||
describe('Gun.chain.fork', function(){
|
||||
var gun = Gun();
|
||||
var fork;
|
||||
it('create fork', function(done){
|
||||
fork = gun.fork().wire();
|
||||
done();
|
||||
});
|
||||
it('put data via fork', function(done){
|
||||
fork.get("fork-test").get("fork").put("test123").once(()=>done());
|
||||
});
|
||||
it('get data via main', function(done){
|
||||
gun.get("fork-test").get("fork").once((data)=>{
|
||||
expect(data).to.be("test123");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('put data via main', function(done){
|
||||
gun.get("fork-test").get("main").put("test321").once(()=>done());
|
||||
});
|
||||
it('get data via fork', function(done){
|
||||
fork.get("fork-test").get("main").once((data)=>{
|
||||
expect(data).to.be("test321");
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
*/
|
||||
(function (Gun, u) {
|
||||
/**
|
||||
*
|
||||
* credits:
|
||||
* github:bmatusiak
|
||||
*
|
||||
*/
|
||||
Gun.chain.fork = function(g) {
|
||||
var gun = this._;
|
||||
var w = {},
|
||||
mesh = () => {
|
||||
var root = gun.root,
|
||||
opt = root.opt;
|
||||
return opt.mesh || Gun.Mesh(root);
|
||||
}
|
||||
w.link = function() {
|
||||
if (this._l) return this._l;
|
||||
this._l = {
|
||||
send: (msg) => {
|
||||
if (!this.l || !this.l.onmessage)
|
||||
throw 'not attached';
|
||||
this.l.onmessage(msg);
|
||||
}
|
||||
}
|
||||
return this._l;
|
||||
};
|
||||
w.attach = function(l) {
|
||||
if (this.l)
|
||||
throw 'already attached';
|
||||
var peer = { wire: l };
|
||||
l.onmessage = function(msg) {
|
||||
mesh().hear(msg.data || msg, peer);
|
||||
};
|
||||
mesh().hi(this.l = l && peer);
|
||||
};
|
||||
w.wire = function(opts) {
|
||||
var f = new Gun(opts);
|
||||
f.fork(w);
|
||||
return f;
|
||||
};
|
||||
if (g) {
|
||||
w.attach(g.link());
|
||||
g.attach(w.link());
|
||||
}
|
||||
return w;
|
||||
};
|
||||
|
||||
|
||||
})((typeof window !== "undefined") ? window.Gun : require('../gun'))
|
18
lib/fsrm.js
Normal file
18
lib/fsrm.js
Normal file
@ -0,0 +1,18 @@
|
||||
var fs = require('fs');
|
||||
var nodePath = require('path');
|
||||
|
||||
var dir = __dirname + '/../';
|
||||
|
||||
module.exports = function rm(path, full) {
|
||||
path = full || nodePath.join(dir, path);
|
||||
if(!fs.existsSync(path)){ return }
|
||||
fs.readdirSync(path).forEach(function(file,index){
|
||||
var curPath = path + "/" + file;
|
||||
if(fs.lstatSync(curPath).isDirectory()) { // recurse
|
||||
rm(null, curPath);
|
||||
} else { // delete file
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(path);
|
||||
};
|
95
lib/hot.js
Normal file
95
lib/hot.js
Normal file
@ -0,0 +1,95 @@
|
||||
;(function(){
|
||||
// on fires when shortcut keydowns or on touch after command selected and then touchdown
|
||||
var m = meta;
|
||||
m.edit({name: "Add", combo: ['A']});
|
||||
m.edit({name: "Row", combo: ['A', 'R'],
|
||||
on: function(eve){
|
||||
m.tap().append('<div class="hold center" style="min-height: 9em; padding: 2%;">');
|
||||
}
|
||||
});
|
||||
m.edit({name: "Columns", combo: ['A','C'],
|
||||
on: function(eve){
|
||||
var on = m.tap(), tmp, c;
|
||||
var html = '<div class="unit col" style="min-height: 9em; padding: 2%;"></div>';
|
||||
if(!on.children('.col').length){ html += html }
|
||||
c = (tmp = on.append(html).children('.col')).length;
|
||||
tmp.each(function(){
|
||||
$(this).css('width', (100/c)+'%');
|
||||
})
|
||||
}
|
||||
});
|
||||
m.edit({name: "Text", combo: ['A','T'],
|
||||
on: function(eve){
|
||||
m.tap().append('<p contenteditable="true">Text</p>');
|
||||
}
|
||||
});
|
||||
m.edit({name: "Drag", combo: ['D']});
|
||||
;(function(){
|
||||
$(document).on('click', function(){
|
||||
var tmp = $('.m-on');
|
||||
if(!tmp.length){ return }
|
||||
tmp.removeClass('m-on');
|
||||
})
|
||||
m.edit({combo: [38], // up
|
||||
on: function(eve){
|
||||
var on = m.tap().removeClass('m-on');
|
||||
on = on.prev().or(on.parent()).or(on);
|
||||
on.addClass('m-on');
|
||||
}, up: function(){
|
||||
}
|
||||
});
|
||||
m.edit({combo: [40], // down
|
||||
on: function(eve){
|
||||
var on = m.tap().removeClass('m-on');
|
||||
on = on.next().or(on.children().first()).or(on);
|
||||
on.addClass('m-on');
|
||||
}, up: function(){
|
||||
}
|
||||
});
|
||||
m.edit({combo: [39], // right
|
||||
on: function(eve){
|
||||
var on = m.tap().removeClass('m-on');
|
||||
on = on.children().first().or(on.next()).or(on.parent()).or(on);
|
||||
on.addClass('m-on');
|
||||
}, up: function(){
|
||||
}
|
||||
});
|
||||
m.edit({combo: [37], // left
|
||||
on: function(eve){
|
||||
var on = m.tap().removeClass('m-on');
|
||||
on = on.parent().or(on);
|
||||
on.addClass('m-on');
|
||||
}, up: function(){
|
||||
}
|
||||
});
|
||||
}());
|
||||
m.edit({name: "Turn", combo: ['T']});
|
||||
m.edit({name: "Size", combo: ['S']});
|
||||
m.edit({name: "X", combo: ['S','X'],
|
||||
on: function(eve){
|
||||
var on = m.tap(), was = on.width();
|
||||
$(document).on('mousemove.tmp', function(eve){
|
||||
var be = was + ((eve.pageX||0) - was);
|
||||
on.css({'max-width': be, width: '100%'});
|
||||
})
|
||||
}, up: function(){ $(document).off('mousemove.tmp') }
|
||||
});
|
||||
m.edit({name: "Y", combo: ['S','Y'],
|
||||
on: function(eve){
|
||||
var on = m.tap(), was = on.height();
|
||||
$(document).on('mousemove.tmp', function(eve){
|
||||
var be = was + ((eve.pageY||0) - was);
|
||||
on.css({'min-height': be});
|
||||
})
|
||||
}, up: function(){ $(document).off('mousemove.tmp') }
|
||||
});
|
||||
m.edit({name: "Fill", combo: ['F'],
|
||||
on: function(eve){
|
||||
var on = m.tap();
|
||||
m.ask('Color name, code, or URL?', function(color){
|
||||
var css = on.closest('p').length? 'color' : 'background';
|
||||
on.css(css, color);
|
||||
});
|
||||
}
|
||||
});
|
||||
}());
|
131
lib/hub.js
Normal file
131
lib/hub.js
Normal file
@ -0,0 +1,131 @@
|
||||
const fs = require('fs');
|
||||
const Gun = require('../index.js');
|
||||
|
||||
const gun = Gun();
|
||||
|
||||
let chokidar;
|
||||
|
||||
try { chokidar = require('chokidar') } catch (error) {
|
||||
} // Must install chokidar to use this feature.
|
||||
|
||||
/**
|
||||
* Watches a directory and send all its content in the database
|
||||
* @constructor
|
||||
* @param {string} what - Which directory hub should watch.
|
||||
* @param {Object} options - https://gun.eco/docs/hub.js#options
|
||||
*/
|
||||
function watch(what, options) {
|
||||
options = options ?? { msg: true, hubignore: false, alias: require('os').userInfo().username }
|
||||
|
||||
options.msg = options.msg ?? true;
|
||||
options.hubignore = options.hubignore ?? false;
|
||||
options.alias = options.alias ?? require('os').userInfo().username
|
||||
|
||||
let modifiedPath = options.alias;
|
||||
|
||||
let watcher;
|
||||
try {
|
||||
|
||||
if (options.hubignore) {
|
||||
|
||||
watcher = chokidar.watch(what, {
|
||||
persistent: true
|
||||
});
|
||||
|
||||
} else if (!options.hubignore) {
|
||||
|
||||
watcher = chokidar.watch(what, {
|
||||
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
||||
persistent: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const log = console.log.bind(console);
|
||||
|
||||
let hubignore;
|
||||
|
||||
// Handle events !
|
||||
watcher
|
||||
.on('add', async function(path) {
|
||||
|
||||
if (options.hubignore && path.includes('.hubignore')) {
|
||||
|
||||
hubignore = fs.readFileSync(what + '/.hubignore', 'utf-8');
|
||||
|
||||
} else if (!path.includes('.hubignore') && !hubignore?.includes(path.substring(path.lastIndexOf("/") + 1))) {
|
||||
|
||||
if (options.msg) log(`File ${path} has been added`);
|
||||
|
||||
if(path[path.search(/^./gm)] === "/" || ".") {
|
||||
gun.get('hub').get(modifiedPath + path.split(require('os').userInfo().username)[1]).put(fs.readFileSync(path, 'utf-8'))
|
||||
} else {
|
||||
gun.get('hub').get(modifiedPath + '/' + path.split(require('os').userInfo().username)[1]).put(fs.readFileSync(path, 'utf-8'))
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if(options.msg) log(`The addition of ${path} has been ignored !`)
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
.on('change', async function(path) {
|
||||
|
||||
if (options.hubignore && path.includes('.hubignore')) {
|
||||
|
||||
hubignore = fs.readFileSync(what + '/.hubignore', 'utf-8');
|
||||
|
||||
} else if (!path.includes('.hubignore') && !hubignore?.includes(path.substring(path.lastIndexOf('/') + 1))) {
|
||||
|
||||
if (options.msg) log(`File ${path} has been changed`);
|
||||
if(path[path.search(/^./gm)] === "/" || ".") {
|
||||
gun.get('hub').get(modifiedPath + path.split(require('os').userInfo().username)[1]).put(fs.readFileSync(path, 'utf-8'))
|
||||
} else {
|
||||
gun.get('hub').get(modifiedPath + '/' + path.split(require('os').userInfo().username)[1]).put(fs.readFileSync(path, 'utf-8'))
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if(options.msg) log(`The changes on ${path} has been ignored.`)
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
.on('unlink', async function (path) {
|
||||
|
||||
if (options.hubignore && path.includes('.hubignore')) {
|
||||
|
||||
hubignore = fs.readFileSync(what + '/.hubignore', 'utf-8');
|
||||
|
||||
} else if (!path.includes('.hubignore') && !hubignore?.includes(path.substring(path.lastIndexOf('/') + 1))) {
|
||||
|
||||
if(options.msg) log(`File ${path} has been removed`);
|
||||
if(path[path.search(/^./gm)] === "/" || ".") {
|
||||
gun.get('hub').get(modifiedPath + path.split(require('os').userInfo().username)[1]).put(null)
|
||||
} else {
|
||||
gun.get('hub').get(modifiedPath + '/' + path.split(require('os').userInfo().username)[1]).put(null)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
if(options.msg) log(`The deletion of ${path} has been ignored!`)
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
if (options.msg) {
|
||||
watcher
|
||||
.on('addDir', path => log(`Directory ${path} has been added`))
|
||||
.on('unlinkDir', path => log(`Directory ${path} has been removed`))
|
||||
.on('error', error => log(`Watcher error: ${error}`))
|
||||
.on('ready', () => log('Initial scan complete. Ready for changes'))
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('If you want to use the hub feature, you must install `chokidar` by typing `npm i chokidar` in your terminal.')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { watch : watch }
|
46
lib/ipfs.js
Normal file
46
lib/ipfs.js
Normal file
@ -0,0 +1,46 @@
|
||||
console.log("IPFS PLUGIN NOT OFFICIALLY MAINTAINED! PROBABLY WON'T WORK! USE AT YOUR OWN RISK! PLEASE CONTRIBUTE FIXES!");
|
||||
var opt = gun._.opt, u;
|
||||
if (u === opt.ipfs.directory) {
|
||||
opt.ipfs.directory = '/gun';
|
||||
}
|
||||
opt.store = {};
|
||||
opt.store.put = function(file, data, cb){
|
||||
var uri = opt.ipfs.directory + '/' + file;
|
||||
opt.ipfs.instance.files.write(uri, Buffer.from(JSON.stringify(data)), {create:true})
|
||||
.then(res => {
|
||||
console.log('File written to IPFS directory', uri, res);
|
||||
return opt.ipfs.instance.files.stat(opt.ipfs.directory, {hash:true});
|
||||
}).then(res => {
|
||||
console.log('Directory hash:', res.hash);
|
||||
return opt.ipfs.instance.name.publish(res.hash);
|
||||
// currently throws "This command must be run in online mode. Try running 'ipfs daemon' first." for some reason, maybe js-ipfs IPNS not ready yet
|
||||
}).then(res => {
|
||||
console.log('IPFS put request successful:', res);
|
||||
cb(undefined, 1);
|
||||
}).catch(error => {
|
||||
console.error('IPFS put request failed', error);
|
||||
});
|
||||
}
|
||||
opt.store.get = function(file, cb){
|
||||
var uri = opt.ipfs.directory + '/' + file;
|
||||
opt.ipfs.instance.files.read(uri, {})
|
||||
.then(res => {
|
||||
var data = JSON.parse(res.toString());
|
||||
console.log(uri + ' was loaded from ipfs:', data);
|
||||
cb(data);
|
||||
});
|
||||
}
|
||||
opt.store.list = function(cb){
|
||||
var stream = opt.ipfs.files.lsReadableStream(opt.ipfs.directory);
|
||||
|
||||
stream.on('data', (file) => {
|
||||
console.log('ls', file.name);
|
||||
if (cb(file.name)) {
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('finish', () => {
|
||||
cb();
|
||||
});
|
||||
}
|
703
lib/ison.js
Normal file
703
lib/ison.js
Normal file
@ -0,0 +1,703 @@
|
||||
/* **************************************************************************
|
||||
* A modified version of yieldable-json package that's backwards compatible
|
||||
* with GunDB's YSON implementation
|
||||
*
|
||||
* (c) Copyright IBM Corp. 2017
|
||||
*
|
||||
* This program and the accompanying materials are made available
|
||||
* under the terms of the Apache License v2.0 which accompanies
|
||||
* this distribution.
|
||||
*
|
||||
* The Apache License v2.0 is available at
|
||||
* http://www.opensource.org/licenses/apache2.0.php
|
||||
*
|
||||
* Contributors:
|
||||
* Multiple authors (IBM Corp.) - initial implementation and documentation
|
||||
* **************************************************************************/
|
||||
|
||||
;(function () {
|
||||
var yson = {}, u;
|
||||
|
||||
let counter = 0;
|
||||
let objStack = [];
|
||||
let temp = '';
|
||||
const limit = 100000;
|
||||
|
||||
function StringifyError(m) {
|
||||
this.name = 'Error';
|
||||
this.message = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking for unicode and backslash characters and replaces if any.
|
||||
* @param { string }
|
||||
* @return { string }
|
||||
*/
|
||||
|
||||
let normalize = (string, flagN) => {
|
||||
let retStr = '';
|
||||
let transform = '';
|
||||
let uc =
|
||||
'/[\\\'\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4' +
|
||||
'\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g';
|
||||
let unicode = new RegExp(uc);
|
||||
// Taking '\\' out of the loop to avoid change in
|
||||
// order of execution of object entries resulting
|
||||
// in unwanted side effect
|
||||
string = string.replace(/\\/gi, '\\\\');
|
||||
let escape = {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"': '\\"',
|
||||
};
|
||||
// Escape is implemented globally
|
||||
for(var pattern in escape) {
|
||||
var regex = new RegExp(pattern,'gi')
|
||||
string = string.replace(regex, escape[pattern])
|
||||
}
|
||||
unicode.lastIndex = 0;
|
||||
if (unicode.test(string)) {
|
||||
// Unicode logic here
|
||||
transform = string.replace(unicode, (a) => {
|
||||
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
if (flagN === 1) {
|
||||
transform += temp;
|
||||
transform += transform;
|
||||
temp = '';
|
||||
return '"' + transform + '"';
|
||||
} else if (flagN === 2) {
|
||||
return '"' + transform + '"';
|
||||
} else {
|
||||
temp += transform;
|
||||
}
|
||||
} else {
|
||||
if (flagN === 1) {
|
||||
retStr += temp;
|
||||
retStr += string;
|
||||
temp = '';
|
||||
return '"' + retStr + '"';
|
||||
} else if (flagN === 2) {
|
||||
return '"' + string + '"';
|
||||
} else {
|
||||
temp += string;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Obtain stringified value by yielding at required intensity
|
||||
* @param { string} field
|
||||
* @param { primitive data type } container
|
||||
* @param { function or array } replacer
|
||||
* @param { number or string } space
|
||||
* @param { number } intensity
|
||||
* @return { function } yieldCPU
|
||||
*/
|
||||
|
||||
function * stringifyYield(field, container, replacer, space, intensity) {
|
||||
let itr = 0;
|
||||
let key = '';
|
||||
let val = '';
|
||||
let length = 0;
|
||||
let tempVal = '';
|
||||
let result = '';
|
||||
let value = container[field];
|
||||
// Made scope local handling async issues
|
||||
let flag1 = 0;
|
||||
let returnStr = '';
|
||||
let subStr = '';
|
||||
let len = 0;
|
||||
|
||||
// Yield the stringification at definite intervals
|
||||
if (++counter > 512 * intensity) {
|
||||
counter = 0;
|
||||
yield val;
|
||||
}
|
||||
|
||||
// Call replacer if one is present (SPEC)
|
||||
if (typeof replacer === 'function') {
|
||||
value = replacer.call(container, field, value);
|
||||
}
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
if (value.length > limit) {
|
||||
for (let l = 0; l < value.length; l += limit) {
|
||||
flag1 = 0;
|
||||
yield value;
|
||||
subStr = value.substr(l, limit);
|
||||
len += subStr.length;
|
||||
if (len === value.length)
|
||||
flag1 = 1;
|
||||
returnStr = normalize(subStr, flag1);
|
||||
}
|
||||
} else
|
||||
returnStr = normalize(value, 2);
|
||||
return returnStr;
|
||||
case 'number':
|
||||
return isFinite(value)
|
||||
? String(value)
|
||||
: 'null';
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
return String(value);
|
||||
case 'undefined':
|
||||
return;
|
||||
case 'function':
|
||||
return 'null';
|
||||
case 'object':
|
||||
if (!value)
|
||||
return 'null';
|
||||
|
||||
// Manage special cases of Arrays and Objects
|
||||
let getResult = (decision) => {
|
||||
if (result.length === 0)
|
||||
if (decision)
|
||||
return '{}';
|
||||
else
|
||||
return '[]';
|
||||
else
|
||||
if (decision)
|
||||
if (space)
|
||||
return '{\n' + space + result.join(',\n' + space) + '\n' + '}';
|
||||
else
|
||||
return '{' + result.join(',') + '}';
|
||||
else
|
||||
if (space)
|
||||
return '[\n' + space + result.join(',\n' + space) + '\n' + ']';
|
||||
else
|
||||
return '[' + result.join(',') + ']';
|
||||
};
|
||||
|
||||
result = [];
|
||||
// If toJSON is present, invoke it (SPEC)
|
||||
if (value && typeof value.toJSON === 'function') {
|
||||
const response = value.toJSON(field);
|
||||
if (response === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof response === "number") {
|
||||
result.push(value.toJSON(field));
|
||||
} else {
|
||||
result.push('"' + value.toJSON(field) + '"');
|
||||
}
|
||||
if (result.length === 0)
|
||||
return '{}';
|
||||
else
|
||||
if (space)
|
||||
return space + result.join(',\n' + space) + '\n';
|
||||
else
|
||||
return result.join(',');
|
||||
}
|
||||
// Array case
|
||||
if (value && value.constructor === Array) {
|
||||
length = value.length;
|
||||
for (itr = 0; itr < length; itr += 1) {
|
||||
tempVal =
|
||||
yield *stringifyYield(itr, value, replacer, space, intensity) ||
|
||||
'null';
|
||||
if (tempVal !== undefined)
|
||||
result.push(tempVal);
|
||||
}
|
||||
return getResult(false);
|
||||
}
|
||||
|
||||
// Manage replacing object scenario (SPEC)
|
||||
if (replacer && typeof replacer === 'object') {
|
||||
length = replacer.length;
|
||||
for (itr = 0; itr < length; itr += 1) {
|
||||
if (typeof replacer[itr] === 'string') {
|
||||
key = replacer[itr];
|
||||
val = yield *stringifyYield(key, value, replacer, space, intensity);
|
||||
if (val !== undefined)
|
||||
result.push(normalize(key, 2) + (space
|
||||
? ': '
|
||||
: ':') + val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Object case
|
||||
objStack.push(value);
|
||||
for (key in value) {
|
||||
if (typeof value[key] === 'object' && value[key] !== null &&
|
||||
value[key] !== undefined) {
|
||||
if (objStack.indexOf(value[key]) !== -1) {
|
||||
return new StringifyError('Circular Structure Detected');
|
||||
} else
|
||||
objStack.push(value[key]);
|
||||
}
|
||||
if (Object.hasOwnProperty.call(value, key)) {
|
||||
val = yield *stringifyYield(key, value, replacer, space, intensity);
|
||||
if (val !== undefined)
|
||||
result.push(normalize(key, 2) + (space
|
||||
? ': '
|
||||
: ':') + val);
|
||||
}
|
||||
objStack = objStack.filter((v, i, a) => { return v !== value[key] });
|
||||
}
|
||||
objStack = objStack.filter((v, i, a) => { return v !== value });
|
||||
}
|
||||
return getResult(true);
|
||||
default:
|
||||
return new StringifyError('Unexpected Character');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling appropriate functions each time.
|
||||
* @param { primitive data types } value
|
||||
* @param { function or array } replacer
|
||||
* @param { number or string } space
|
||||
* @param { number } intensity
|
||||
* @param { function } callback
|
||||
* @return { function } yieldCPU
|
||||
*/
|
||||
|
||||
let stringifyWrapper = (value, replacer, space, intensity, callback) => {
|
||||
let indent = '';
|
||||
if (typeof space === 'number') {
|
||||
indent = ' '.repeat(space);
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
let yielding;
|
||||
|
||||
// To hold 'stringifyYield' genarator function
|
||||
function * yieldBridge() {
|
||||
yielding = yield *stringifyYield('', {'': value}, replacer, indent, 1);
|
||||
}
|
||||
|
||||
let rs = yieldBridge();
|
||||
let g = rs.next();
|
||||
|
||||
let yieldCPU = () => {
|
||||
setTimeout(() => {
|
||||
g = rs.next();
|
||||
if (g && g.done === true) {
|
||||
// Reinitializing the values at the end of API call
|
||||
counter = 0;
|
||||
temp = ''
|
||||
objStack = [];
|
||||
if (typeof yielding === 'object')
|
||||
return callback(yielding, null);
|
||||
else
|
||||
return callback(null, yielding);
|
||||
}
|
||||
yieldCPU();
|
||||
}, 0);
|
||||
};
|
||||
return yieldCPU();
|
||||
};
|
||||
|
||||
/**
|
||||
* This method parses a JSON text to produce an object or array.
|
||||
* It can throw a SyntaxError exception, if the string is malformed.
|
||||
* @param { string } text
|
||||
* @param { function or array } reviver
|
||||
* @param { number } intensity
|
||||
* @param { function } cb
|
||||
* @return { function } yieldCPU
|
||||
*/
|
||||
let parseWrapper = (text, reviver, intensity, cb) => {
|
||||
let counter = 0;
|
||||
let keyN = 0;
|
||||
let parseStr = text;
|
||||
let at = 0;
|
||||
let ch = ' ';
|
||||
let word = '';
|
||||
function ParseError(m) {
|
||||
this.name = 'ParseError';
|
||||
this.message = m;
|
||||
this.text = parseStr;
|
||||
}
|
||||
|
||||
// Seek to the next character, after skipping white spaces, if any.
|
||||
let seek = () => {
|
||||
ch = parseStr.charAt && parseStr.charAt(at);
|
||||
at++;
|
||||
while (ch && ch <= ' ') {
|
||||
seek();
|
||||
}
|
||||
return ch;
|
||||
};
|
||||
|
||||
// Seek to the previous character, required in some special cases.
|
||||
let unseek = () => {
|
||||
ch = parseStr.charAt(--at);
|
||||
};
|
||||
|
||||
// Match 'true', 'false' and 'null' built-ins.
|
||||
let wordCheck = () => {
|
||||
word = '';
|
||||
do {
|
||||
word += ch;
|
||||
seek();
|
||||
} while (ch.match(/[a-z]/i));
|
||||
parseStr = parseStr.slice(at - 1);
|
||||
at = 0;
|
||||
return word;
|
||||
};
|
||||
|
||||
// Process strings specially.
|
||||
let normalizeUnicodedString = () => {
|
||||
let inQuotes = ' ';
|
||||
let tempIndex = at;
|
||||
let index = 0;
|
||||
let slash = 0;
|
||||
let c = '"';
|
||||
while (c) {
|
||||
index = parseStr.indexOf('"', tempIndex + 1);
|
||||
tempIndex = index;
|
||||
ch = parseStr.charAt(tempIndex - 1);
|
||||
while (ch === '\\') {
|
||||
slash++;
|
||||
ch = parseStr.charAt(tempIndex - (slash + 1));
|
||||
}
|
||||
if (slash % 2 === 0) {
|
||||
inQuotes = parseStr.substring(at, index);
|
||||
parseStr = parseStr.slice(++index);
|
||||
slash = 0;
|
||||
break;
|
||||
} else
|
||||
slash = 0;
|
||||
}
|
||||
|
||||
// When parsing string values, look for " and \ characters.
|
||||
index = inQuotes.indexOf('\\');
|
||||
while (index >= 0) {
|
||||
let escapee = {
|
||||
'"': '"',
|
||||
'\'': '\'',
|
||||
'/': '/',
|
||||
'\\': '\\',
|
||||
b: '\b',
|
||||
f: '\f',
|
||||
n: '\n',
|
||||
r: '\r',
|
||||
t: '\t',
|
||||
};
|
||||
let hex = 0;
|
||||
let i = 0;
|
||||
let uffff = 0;
|
||||
at = index;
|
||||
ch = inQuotes.charAt(++at);
|
||||
if (ch === 'u') {
|
||||
uffff = 0;
|
||||
for (i = 0; i < 4; i += 1) {
|
||||
hex = parseInt(ch = inQuotes.charAt(++at), 16);
|
||||
if (!isFinite(hex)) {
|
||||
break;
|
||||
}
|
||||
uffff = uffff * 16 + hex;
|
||||
}
|
||||
inQuotes = inQuotes.slice(0, index) +
|
||||
String.fromCharCode(uffff) + inQuotes.slice(index + 6);
|
||||
at = index;
|
||||
} else if (typeof escapee[ch] === 'string') {
|
||||
inQuotes = inQuotes.slice(0, index) +
|
||||
escapee[ch] + inQuotes.slice(index + 2);
|
||||
at = index + 1;
|
||||
} else
|
||||
break;
|
||||
index = inQuotes.indexOf('\\', at);
|
||||
}
|
||||
at = 0;
|
||||
return inQuotes;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function parses the current string and returns the JavaScript
|
||||
* Object, through recursive method, and yielding back occasionally
|
||||
* based on the intensity parameter.
|
||||
* @return { object } returnObj
|
||||
*/
|
||||
function * parseYield() {
|
||||
let key = '';
|
||||
let returnObj = {};
|
||||
let returnArr = [];
|
||||
let v = '';
|
||||
let inQuotes = '';
|
||||
let num = 0;
|
||||
let numHolder = '';
|
||||
let addup = () => {
|
||||
numHolder += ch;
|
||||
seek();
|
||||
};
|
||||
// Handle premitive types. eg: JSON.parse(21)
|
||||
if (typeof parseStr === 'number' || typeof parseStr === 'boolean' || typeof parseStr === "function" ||
|
||||
parseStr === null) {
|
||||
parseStr = '';
|
||||
return text;
|
||||
} else if (typeof parseStr === 'undefined') {
|
||||
parseStr = undefined;
|
||||
return text;
|
||||
} else if (parseStr.charAt && parseStr.charAt(0) === '[' && parseStr.charAt(1) === ']') {
|
||||
parseStr = '';
|
||||
return [];
|
||||
} else if (parseStr.charAt && parseStr.charAt(0) === '{' && parseStr.charAt(1) === '}') {
|
||||
parseStr = '';
|
||||
return {};
|
||||
} else {
|
||||
// Yield the parsing work at specified intervals.
|
||||
if (++counter > 512 * intensity) {
|
||||
counter = 0;
|
||||
yield;
|
||||
}
|
||||
// Common case: non-premitive types.
|
||||
if (keyN !== 1)
|
||||
seek();
|
||||
switch (ch) {
|
||||
case '{':
|
||||
// Object case
|
||||
seek();
|
||||
if (ch === '}') {
|
||||
parseStr = parseStr.slice(at);
|
||||
at = 0;
|
||||
return returnObj;
|
||||
}
|
||||
do {
|
||||
if (ch !== '"')
|
||||
seek();
|
||||
keyN = 1;
|
||||
key = yield *parseYield();
|
||||
keyN = 0;
|
||||
seek();
|
||||
returnObj[key] = yield *parseYield();
|
||||
seek();
|
||||
if (ch === '}') {
|
||||
parseStr = parseStr.slice(at);
|
||||
at = 0;
|
||||
return returnObj;
|
||||
}
|
||||
} while (ch === ',');
|
||||
return new ParseError('Bad object');
|
||||
case '[':
|
||||
// Array case
|
||||
seek();
|
||||
if (ch === ']') {
|
||||
parseStr = parseStr.slice(at);
|
||||
at = 0;
|
||||
return returnArr;
|
||||
}
|
||||
unseek();
|
||||
do {
|
||||
v = yield *parseYield();
|
||||
returnArr.push(v);
|
||||
seek();
|
||||
if (ch === ']') {
|
||||
parseStr = parseStr.slice(at);
|
||||
at = 0;
|
||||
return returnArr;
|
||||
}
|
||||
} while (ch === ',');
|
||||
return new ParseError('Bad array');
|
||||
case '"':
|
||||
parseStr = parseStr.slice(at - 1);
|
||||
at = 0;
|
||||
if (parseStr.charAt(0) === '"' && parseStr.charAt(1) === '"') {
|
||||
parseStr = parseStr.slice(2);
|
||||
at = 0;
|
||||
return inQuotes;
|
||||
} else {
|
||||
seek();
|
||||
return normalizeUnicodedString();
|
||||
}
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
if (ch === '-') addup();
|
||||
do {
|
||||
addup();
|
||||
if (ch === '.' || ch === 'e' || ch === 'E' ||
|
||||
ch === '-' || ch === '+' ||
|
||||
(ch >= String.fromCharCode(65) &&
|
||||
ch <= String.fromCharCode(70)))
|
||||
addup();
|
||||
} while (ch === '-' || ch === '+' || (isFinite(ch) && ch !== ''));
|
||||
num = Number(numHolder);
|
||||
parseStr = parseStr.slice(at - 1);
|
||||
at = 0;
|
||||
return num;
|
||||
case 't':
|
||||
word = wordCheck();
|
||||
if (word === 'true')
|
||||
return true;
|
||||
else return new ParseError('Unexpected character');
|
||||
case 'f':
|
||||
word = wordCheck();
|
||||
if (word === 'false')
|
||||
return false;
|
||||
else return new ParseError('Unexpected character');
|
||||
case 'n':
|
||||
word = wordCheck();
|
||||
if (word === 'null')
|
||||
return null;
|
||||
else return new ParseError('Unexpected character');
|
||||
default:
|
||||
return new ParseError('Unexpected character');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a reviver function, we recursively walk the new structure,
|
||||
* passing each name/value pair to the reviver function for possible
|
||||
* transformation, starting with a temporary root object that holds the result
|
||||
* in an empty key. If there is not a reviver function, we simply return the
|
||||
* result.
|
||||
* @param { object } yieldedObject
|
||||
* @param { string } key
|
||||
* @return { function } reviver
|
||||
*/
|
||||
let revive = (yieldedObject, key) => {
|
||||
let k = '';
|
||||
let v = '';
|
||||
let val = yieldedObject[key];
|
||||
if (val && typeof val === 'object') {
|
||||
for (k in val) {
|
||||
if (Object.prototype.hasOwnProperty.call(val, k)) {
|
||||
v = revive(val, k);
|
||||
if (v !== undefined)
|
||||
val[k] = v;
|
||||
else
|
||||
delete val[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(yieldedObject, key, val);
|
||||
};
|
||||
|
||||
let yielding = '';
|
||||
// To hold 'parseYield' genarator function
|
||||
function * yieldBridge() {
|
||||
yielding = yield* parseYield();
|
||||
}
|
||||
let rs = yieldBridge();
|
||||
let gen = rs.next();
|
||||
|
||||
// Main yield control logic.
|
||||
let yieldCPU = () => {
|
||||
setTimeout(() => {
|
||||
gen = rs.next();
|
||||
|
||||
if (gen && gen.done === true) {
|
||||
let isEmpty = (value) => {
|
||||
if (value.charAt(0) === '}' || value.charAt(0) === ']')
|
||||
value = value.substring(1, value.length);
|
||||
return typeof value === 'string' && !value.trim();
|
||||
};
|
||||
if (typeof yielding === 'undefined')
|
||||
return cb(new ParseError('Unexpected Character'), null);
|
||||
else if (yielding instanceof ParseError)
|
||||
return cb(yielding, null);
|
||||
else if (!isEmpty(parseStr))
|
||||
return cb(new ParseError('Unexpected Character'), null);
|
||||
else {
|
||||
if (reviver !== null) {
|
||||
if (typeof reviver === 'function') {
|
||||
let result = revive({'': yielding}, '');
|
||||
return cb(null, result);
|
||||
}
|
||||
} else
|
||||
return cb(null, yielding);
|
||||
}
|
||||
}
|
||||
yieldCPU();
|
||||
}), 0;
|
||||
};
|
||||
return yieldCPU();
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the provided space
|
||||
* @param { string or number } space
|
||||
* @return { string or number }
|
||||
*/
|
||||
let validateSpace = (space) => {
|
||||
if (typeof space === 'number') {
|
||||
space = Math.round(space);
|
||||
if (space >= 1 && space <= 10)
|
||||
return space;
|
||||
else if (space < 1)
|
||||
return 0;
|
||||
else
|
||||
return 10;
|
||||
} else {
|
||||
if (space.length <= 10)
|
||||
return space;
|
||||
else
|
||||
return space.substr(0, 9);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the provided intensity
|
||||
* @param { number } intensity
|
||||
* @return { number }
|
||||
*/
|
||||
let validateIntensity = (intensity) => {
|
||||
intensity = Math.round(intensity);
|
||||
if (intensity > 0 && intensity <= 32)
|
||||
return intensity;
|
||||
else if (intensity <= 0)
|
||||
return 1;
|
||||
else
|
||||
return 32;
|
||||
};
|
||||
|
||||
yson.parseAsync = function (data, callback, reviver = null, intensity = 1) {
|
||||
//Bring parity with the in-built parser, that takes both string and buffer
|
||||
if (Buffer.isBuffer(data))
|
||||
data = data.toString();
|
||||
|
||||
if (!callback)
|
||||
throw new Error('Missing Callback');
|
||||
|
||||
|
||||
intensity = validateIntensity(intensity);
|
||||
return parseWrapper(data, reviver, intensity, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Error checking and call of appropriate functions for JSON stringify API
|
||||
* @param { primitive data types } data
|
||||
* @param { function or array } replacer
|
||||
* @param { number or string } space
|
||||
* @param { number } intensity
|
||||
* @param { function } callback
|
||||
* @return { function } stringifyWrapper
|
||||
*/
|
||||
yson.stringifyAsync = function(data, callback, replacer = null, space, intensity = 1) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new TypeError('Callback is not a function');
|
||||
}
|
||||
if (typeof space === 'number' || typeof space === 'string')
|
||||
space = validateSpace(space);
|
||||
if (typeof intensity === 'number')
|
||||
intensity = validateIntensity(intensity);
|
||||
return stringifyWrapper(data, replacer, space, intensity, callback);
|
||||
}
|
||||
|
||||
if(typeof window != ''+u){ window.YSON = yson }
|
||||
try{ if(typeof module != ''+u){ module.exports = yson } }catch(e){}
|
||||
if(typeof JSON != ''+u){
|
||||
JSON.parseAsync = yson.parseAsync;
|
||||
JSON.stringifyAsync = yson.stringifyAsync;
|
||||
}
|
||||
|
||||
}());
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user