Compare commits
691 Commits
v8.0.1
...
main-chapr
Author | SHA1 | Date | |
---|---|---|---|
a461444bed | |||
1307f87912 | |||
23cf858a02 | |||
b49146b36f | |||
8d67dce865 | |||
543807aa50 | |||
|
82a8c3f9fc | ||
|
3e680e88d8 | ||
|
005f5374f0 | ||
|
79bb8e76ce | ||
|
61192f91d9 | ||
|
e31d4c7bac | ||
|
c30569dfd3 | ||
|
5e02b9bd5d | ||
|
9114db8764 | ||
|
bc7621c25d | ||
|
ae518aa2c3 | ||
|
e4a4b2819c | ||
|
5310021b67 | ||
|
e18fdd56b1 | ||
|
94963662e7 | ||
|
4f14d50f5d | ||
|
699ab71f21 | ||
|
daeb641530 | ||
|
6e71555ee4 | ||
|
27d61411ab | ||
|
3328e1c94c | ||
|
c63c787a6d | ||
|
0efeead71a | ||
|
5822fade32 | ||
|
621612d126 | ||
|
536350155d | ||
|
841a57fd66 | ||
|
175595098a | ||
|
03655b45b2 | ||
|
fa9ae16ead | ||
|
cb1f929045 | ||
|
9ba339a6d9 | ||
|
bc452596a7 | ||
|
2b732cdb94 | ||
|
1e4937bf41 | ||
|
8b28ffecfe | ||
|
80bf81db6f | ||
|
7fdbcd0e64 | ||
|
975a2c2305 | ||
|
abad4d054b | ||
|
b5b6d1f8b2 | ||
|
64aef7f70d | ||
|
4f0f210e21 | ||
|
c4299e92be | ||
|
0cc03e7e7a | ||
|
f9f246282b | ||
|
3e4a8b23fe | ||
|
cdda140137 | ||
|
0a15b003dc | ||
|
23539ecf47 | ||
|
efa448956e | ||
|
743f336dc5 | ||
|
8a0386fdcc | ||
|
b647f5463f | ||
|
46f2e40f91 | ||
|
8b1b4b6446 | ||
|
9407d00c67 | ||
|
f0d584aed6 | ||
|
2768c2a3ec | ||
|
8fb1716681 | ||
|
507cfa9ef2 | ||
|
5ef7807a68 | ||
|
952d3cf440 | ||
|
c9a95faa35 | ||
|
02185cee5f | ||
|
40290f27e7 | ||
|
bbfcb74eaa | ||
|
a2e618a8f6 | ||
|
ebbb39a9aa | ||
|
2d66a974d0 | ||
|
fa12dae7bc | ||
|
df25e16438 | ||
|
c39d61f62c | ||
|
01ce344d82 | ||
|
bb428fa1d7 | ||
|
5a710e4601 | ||
|
f91f4cb7ab | ||
|
f2ac748e06 | ||
|
27a47c6ee8 | ||
|
0a338feb40 | ||
|
d74daccfbb | ||
|
5c3acc8a4d | ||
|
48e7987f38 | ||
|
4d2a8e9f8d | ||
|
3d0e9a7374 | ||
|
fb054c40be | ||
|
c3c666e568 | ||
|
6b365458f5 | ||
|
885a3d92b5 | ||
|
6df80507f3 | ||
|
16754c15aa | ||
|
70a10efd49 | ||
|
9a63629b40 | ||
|
dcfc0d50f4 | ||
|
fde55bea2c | ||
|
3530ccc35d | ||
|
05c5cd1046 | ||
|
c38c706079 | ||
|
e1e93c2ec9 | ||
|
16b442ac19 | ||
|
0122bd7bab | ||
|
56259fd22e | ||
|
85181053e6 | ||
|
26062df4e5 | ||
|
2fb9fec8f8 | ||
|
d4cb67dc5f | ||
|
ad7b4ae343 | ||
|
57f489f61b | ||
|
6ce8879e9c | ||
|
1ee4cce2fd | ||
|
b69e5b5482 | ||
|
3f6ad0d950 | ||
|
9899351597 | ||
|
0b3bcbfe40 | ||
|
5e3139f563 | ||
|
7b8b32638c | ||
|
8035084e8e | ||
|
19466ddf62 | ||
|
bb7ed5315f | ||
|
6719aeba45 | ||
|
45844447bf | ||
|
82314eb1df | ||
|
d9c86ed575 | ||
|
89d231a5dd | ||
|
72406b4aac | ||
|
e08b58c3d3 | ||
|
387c992381 | ||
|
4e98383e65 | ||
|
dae84028c2 | ||
|
5e5bdc78ec | ||
|
15c10376b0 | ||
|
ccc165facc | ||
|
aa299af3ec | ||
|
b16e3efbd4 | ||
|
5db3e8ca51 | ||
|
0fcdb2a594 | ||
|
5029d93523 | ||
|
bab11b682b | ||
|
9343488864 | ||
|
782c8c97fa | ||
|
d2f7756313 | ||
|
07371b0852 | ||
|
25aa0303c6 | ||
|
6186f05a3e | ||
|
dd4f1e6b85 | ||
|
366390935f | ||
|
7eb07fa02f | ||
|
fbe2e41de7 | ||
|
cfc2d93b9b | ||
|
12170461cd | ||
|
ab079036da | ||
|
8001cf3809 | ||
|
d93a47d246 | ||
|
76fb90319b | ||
|
9ca1114670 | ||
|
251784c21c | ||
|
8c3ce2892b | ||
|
6c44fb2a2f | ||
|
465aa38222 | ||
|
6fc83db9df | ||
|
a486fe25b8 | ||
|
87c172acd0 | ||
|
968ec8182c | ||
|
451ed18261 | ||
|
078936fb8a | ||
|
0227b2e06c | ||
|
44c22bb984 | ||
|
da12005420 | ||
|
de65409e79 | ||
|
9ab99a0859 | ||
|
77fdbb5aa2 | ||
|
a8f4bc4617 | ||
|
18cd864e09 | ||
|
192861f9c9 | ||
|
4a43b46432 | ||
|
95bf14385b | ||
|
0fe2df24ad | ||
|
5a368a9c0f | ||
|
73b9cbea13 | ||
|
2dee891c63 | ||
|
97b87bcdc1 | ||
|
c10f18ad1c | ||
|
4d687b022a | ||
|
53d562481e | ||
|
7539fc1a6f | ||
|
f740332e95 | ||
|
4abc9c45d3 | ||
|
756942d2bd | ||
|
fdcab413f5 | ||
|
3c6dd5d0ae | ||
|
3004d7bff5 | ||
|
6494b34da3 | ||
|
447fe8ba08 | ||
|
8ab0b718de | ||
|
97e5bb9b11 | ||
|
5f38a914b1 | ||
|
fa132567da | ||
|
5ea00b8008 | ||
|
a76393f216 | ||
|
739d79e90c | ||
|
e63ba2075f | ||
|
8ca265d8d5 | ||
|
78a7841afb | ||
|
4733e7f65f | ||
|
8c84e276ff | ||
|
a251608fc5 | ||
|
3af6168270 | ||
|
34c17a7662 | ||
|
87aba699f1 | ||
|
defe5cdcb0 | ||
|
038a38cae0 | ||
|
8aaf792c6c | ||
|
659a69e7b7 | ||
|
ec70d4bc56 | ||
|
b0b8a3f89d | ||
|
72be204f77 | ||
|
d5deed0871 | ||
|
040d5bae9d | ||
|
b2ad6e844c | ||
|
b4fb710d61 | ||
|
a5be19ad50 | ||
|
6250be89ed | ||
|
e355814da7 | ||
|
1ecffd61af | ||
|
d887ea4b7b | ||
|
b2943351a1 | ||
|
0b514fbc3e | ||
|
0d743d428f | ||
|
0b809049e2 | ||
|
8b64482fd4 | ||
|
996e47b275 | ||
|
c3ff3370bd | ||
|
83726f0821 | ||
|
1ef2c3b3d4 | ||
|
0af4b185ac | ||
|
a3a90a9be8 | ||
|
7861af7bf9 | ||
|
e90cf6453e | ||
|
dbd36a0d75 | ||
|
c6bc5e1438 | ||
|
cc6a20b3e6 | ||
|
40024f4599 | ||
|
18d8b69f00 | ||
|
62cd0afa37 | ||
|
eb9fd74c77 | ||
|
6c9f0400c8 | ||
|
210d4cb87d | ||
|
7b6301d7f3 | ||
|
63cc8411a5 | ||
|
21c41f9265 | ||
|
83351fb98f | ||
|
4531dd4363 | ||
|
31b10aa0b0 | ||
|
ef16a52ced | ||
|
1a8ae3dcbe | ||
|
34a4a70ae2 | ||
|
f791169f47 | ||
|
782de0165f | ||
|
e555469e3f | ||
|
b4eb6c0994 | ||
|
74cda3f4b4 | ||
|
aebd98cf0a | ||
|
983d5ea09c | ||
|
3720092c09 | ||
|
36cb7a644e | ||
|
6293efd8ee | ||
|
5760379e0a | ||
|
a4ee3085b7 | ||
|
bd60861c1e | ||
|
fbe86e5af8 | ||
|
927add0707 | ||
|
8378ce739b | ||
|
f9e6c3ff95 | ||
|
98ae55790d | ||
|
bb0ad432b2 | ||
|
55e3417df3 | ||
|
92f71bafb2 | ||
|
f1734dbb40 | ||
|
0489e0f902 | ||
|
0aa66fee99 | ||
|
ed1099490c | ||
|
a74bbd093f | ||
|
8ae4b6abd4 | ||
|
ca3c8fc10b | ||
|
e8b9681a15 | ||
|
51516e38f6 | ||
|
de72d6bae3 | ||
|
0d9561e666 | ||
|
97be0bd8ac | ||
|
7f99b24e28 | ||
|
17e5804be7 | ||
|
fd9e41a917 | ||
|
760f4f7dd1 | ||
|
5231262bda | ||
|
34927b5b77 | ||
|
7028286855 | ||
|
342c75775b | ||
|
f1cc8c85f4 | ||
|
c5588e3c49 | ||
|
3d19def3e7 | ||
|
53521c2236 | ||
|
22170450b4 | ||
|
f014db8b7a | ||
|
fe9345b7fc | ||
|
d2bdce51e8 | ||
|
cac92c3ebc | ||
|
603f8349e8 | ||
|
5f6e70289b | ||
|
c716551fc8 | ||
|
78634cdec3 | ||
|
45e989f048 | ||
|
0cfe2a18af | ||
|
71d1383604 | ||
|
0df1c1880e | ||
|
06a2aac022 | ||
|
d8ea42a845 | ||
|
085de8405f | ||
|
990aefc6cb | ||
|
eb29d962f5 | ||
|
605c02901a | ||
|
368bcf258f | ||
|
07efd601da | ||
|
4aab83c4af | ||
|
f05a41f6a5 | ||
|
4d9c0d3f9a | ||
|
35bdcf58cd | ||
|
3ca478da32 | ||
|
5d3da20bd9 | ||
|
a95c070c2b | ||
|
c14c2b3b59 | ||
|
b7e793df02 | ||
|
c245599e65 | ||
|
7e86c561c7 | ||
|
25891ef0ee | ||
|
ba7c6d7a50 | ||
|
7e2dd55c16 | ||
|
b8579ccc90 | ||
|
73a7b209ce | ||
|
8dc8b1d564 | ||
|
d2b4f2e0f6 | ||
|
320f11f795 | ||
|
4237e5b3ae | ||
|
8671afc4b1 | ||
|
6d39fae71a | ||
|
0316f073e0 | ||
|
1744dbc420 | ||
|
eb8ebea920 | ||
|
6c13cfaf30 | ||
|
a228cf244c | ||
|
3b124cfdce | ||
|
4c8bc187b2 | ||
|
1ad6de2dd6 | ||
|
795a9a7e3e | ||
|
87ecf061f1 | ||
|
858a6051ac | ||
|
52693bfc0b | ||
|
79063b9993 | ||
|
22840f8a50 | ||
|
92f74b888b | ||
|
ac241e5ed1 | ||
|
6573d080e4 | ||
|
12a0d0e3cc | ||
|
2a7bfd13b5 | ||
|
ab7e879261 | ||
|
a57853156e | ||
|
a3c0f90476 | ||
|
b5bcc05947 | ||
|
bd579d8613 | ||
|
0c02fbe4cf | ||
|
0b172be3d0 | ||
|
7e9861ba8f | ||
|
0c9fd691ac | ||
|
984386aa64 | ||
|
ae2bd63d8f | ||
|
5e27e27845 | ||
|
ca9d68b99e | ||
|
99c5d7a760 | ||
|
4b6626ee70 | ||
|
8ea9e8a5ad | ||
|
f929f8b7f9 | ||
|
ad8c250314 | ||
|
02146bdf22 | ||
|
dbc0028880 | ||
|
f27238b76f | ||
|
5adc39ebac | ||
|
53ccc4948f | ||
|
e48bd0dd4b | ||
|
cb6fc463aa | ||
|
3b4ef074b9 | ||
|
a3b5c58023 | ||
|
1f7fe4f48e | ||
|
e03d7bc71c | ||
|
828b6785b4 | ||
|
e06d335b7d | ||
|
55d02cb37b | ||
|
8e612e3cd9 | ||
|
0af72d41da | ||
|
ebd9474ac1 | ||
|
2172409fec | ||
|
d22c063ae5 | ||
|
b71a7ae2ac | ||
|
6c26c1397f | ||
|
a31f87f25b | ||
|
18ca225450 | ||
|
fe10f2203b | ||
|
3c2eda4a1d | ||
|
0a07cad2cb | ||
|
f93b30f7b3 | ||
|
35b2c247f3 | ||
|
1915d70c41 | ||
|
059795c0f3 | ||
|
0260e5f803 | ||
|
1b60697565 | ||
|
fb158e418b | ||
|
995cc51f99 | ||
|
760fa4ae63 | ||
|
d4382fda19 | ||
|
4c394c072e | ||
|
699326547a | ||
|
2a9a01bc32 | ||
|
ce22508344 | ||
|
230b72139a | ||
|
0b168e8439 | ||
|
e492885ac0 | ||
|
7cb86638b5 | ||
|
2d5b0753e2 | ||
|
8e1c3e47df | ||
|
99ae9a9850 | ||
|
7b31f4d333 | ||
|
8930201b99 | ||
|
ad11dce92f | ||
|
0c049dd364 | ||
|
ca0953e4bd | ||
|
a17e8bea1c | ||
|
b331405658 | ||
|
c3e3f89e52 | ||
|
f0297fe075 | ||
|
4d4e0ffc8d | ||
|
e202ac9964 | ||
|
5c5825fb75 | ||
|
e0e1fd4f02 | ||
|
5e4fa98577 | ||
|
7e18873bcd | ||
|
592d982210 | ||
|
9d4382c754 | ||
|
b1a448f1e9 | ||
|
af342456c5 | ||
|
5d3895078b | ||
|
0464381cac | ||
|
32a443408a | ||
|
b429484501 | ||
|
2fa1d00fb4 | ||
|
c590c4f4bb | ||
|
6b1845ff39 | ||
|
fb704fdf87 | ||
|
f6a985c24f | ||
|
44cd910154 | ||
|
f21fc90629 | ||
|
21fb57ecfd | ||
|
3bb645ce24 | ||
|
fd991846f4 | ||
|
523566c218 | ||
|
cd1c6a4d23 | ||
|
c3933426b9 | ||
|
be561addfa | ||
|
c379fe80fb | ||
|
5ef0f3ad5d | ||
|
d570b6095e | ||
|
fca275b7c9 | ||
|
e160ee2ed5 | ||
|
70aa22d12d | ||
|
fbfd6c3ac4 | ||
|
7355c2c5fe | ||
|
297869c59f | ||
|
defea1631c | ||
|
82fb263438 | ||
|
d2b9c954d9 | ||
|
f9a9d327d1 | ||
|
d2622f6fed | ||
|
731e98b07c | ||
|
89a3c81a19 | ||
|
2133241998 | ||
|
47b016599e | ||
|
e52056bb33 | ||
|
1c0ce25f12 | ||
|
c42bd90c9d | ||
|
7409668a5d | ||
|
d432bab73a | ||
|
9328db645d | ||
|
21d3159081 | ||
|
b7ad1eaade | ||
|
727bad01c7 | ||
|
b248803a4b | ||
|
a468a0fb24 | ||
|
c2ba8ecf97 | ||
|
7b63b7b30f | ||
|
ca20f687bf | ||
|
bbef09346a | ||
|
1c8085f00e | ||
|
beb220f188 | ||
|
20cd90855f | ||
|
39e9ed8fe4 | ||
|
22a7e1b35f | ||
|
9a95c2d048 | ||
|
9a2424fa26 | ||
|
b44e1c82f0 | ||
|
a07bd7c817 | ||
|
85d75a5494 | ||
|
a2c7e83812 | ||
|
7c7b667745 | ||
|
e1a919749f | ||
|
8dff87658f | ||
|
38519a6261 | ||
|
f6c524f760 | ||
|
94d29bc617 | ||
|
0fb4dcac96 | ||
|
caf8360745 | ||
|
ea63c9a17d | ||
|
6a293de86b | ||
|
5caa5aa9ae | ||
|
b8487cfaa6 | ||
|
203afea72b | ||
|
7f40d1a2f2 | ||
|
47f3109957 | ||
|
066d29d637 | ||
|
12a2354d9e | ||
|
d8f6a16936 | ||
|
8a9e0f6d23 | ||
|
f4fdc36d31 | ||
|
ba52defdae | ||
|
505416a59e | ||
|
9d250c3cbf | ||
|
46e6cfb0d4 | ||
|
3d8a583101 | ||
|
337638034f | ||
|
0f648ed1eb | ||
|
d3fa925bff | ||
|
cb0b1765f3 | ||
|
bef2cbb462 | ||
|
125f45c447 | ||
|
817b1200ca | ||
|
8dcacd41bf | ||
|
2e9f478c87 | ||
|
b13c674047 | ||
|
dd539c7675 | ||
|
4f89c4c10f | ||
|
818ad0955a | ||
|
ac473fd297 | ||
|
11bbbb4899 | ||
|
0d0ee294ad | ||
|
7c51147023 | ||
|
003a90c71c | ||
|
d06761e2a8 | ||
|
c83cc69cef | ||
|
351212cd13 | ||
|
93ec6e4e83 | ||
|
007b4bce2f | ||
|
46a5a64dda | ||
|
72eb9d9595 | ||
|
ba6da97416 | ||
|
fab83139f8 | ||
|
6cd05eab39 | ||
|
16626c80a7 | ||
|
c809141ee6 | ||
|
53df0f8942 | ||
|
17678f06f6 | ||
|
5da069d585 | ||
|
10264e1647 | ||
|
900bb11fb0 | ||
|
1b3ab06614 | ||
|
a20188e7a8 | ||
|
d55fc92c1c | ||
|
0a9444c41b | ||
|
7ebb35d80c | ||
|
cada8146b6 | ||
|
68bcf51ed8 | ||
|
ed089d0546 | ||
|
6ee3ddbc8c | ||
|
c3aafdf341 | ||
|
9411860f69 | ||
|
4dc78c3742 | ||
|
ee4ab47442 | ||
|
4bb4f932cf | ||
|
f25f110e0e | ||
|
e9ae5306e3 | ||
|
8ad097f23f | ||
|
048560908e | ||
|
0dee9897dd | ||
|
a673086941 | ||
|
b16d035975 | ||
|
ca9e13a047 | ||
|
dc66c3a101 | ||
|
47bab27f7b | ||
|
077162529d | ||
|
32bc8917ab | ||
|
b418db5643 | ||
|
1c247969b4 | ||
|
c178cbbcd8 | ||
|
d31f1ce317 | ||
|
209bea2fcc | ||
|
c820920b2c | ||
|
ed7efb2c61 | ||
|
801a21e049 | ||
|
a8a397d8b2 | ||
|
bdc7390eca | ||
|
0f99d4833e | ||
|
b96150a629 | ||
|
6001273c6f | ||
|
430f1f5a0e | ||
|
128079f2a4 | ||
|
8efafc7c25 | ||
|
20666a334e | ||
|
c81841e036 | ||
|
3d74ed31c2 | ||
|
1a6a7ade73 | ||
|
ee4e056125 | ||
|
e4da0f894d | ||
|
284eccf047 | ||
|
bad2577e5e | ||
|
9b1a7c70a3 | ||
|
a06d180827 | ||
|
2af758e66d | ||
|
fa562cabae | ||
|
07b2425ff9 | ||
|
d48d748e30 | ||
|
33f3cb76ad | ||
|
78846ab984 | ||
|
afbb46bd8c | ||
|
ed63902ac1 | ||
|
35947e3d62 | ||
|
bdac6f1b47 | ||
|
c76e6fb86b | ||
|
664f290001 | ||
|
c62ea03e2e | ||
|
8db12e61bf | ||
|
f36c3fefdd | ||
|
a5b73f0309 | ||
|
e347621dc8 | ||
|
1ee75b78b7 | ||
|
571cb3bac1 | ||
|
5dd0065936 | ||
|
ecfc3e9fcf | ||
|
a60127e66f | ||
|
93e1758a0b | ||
|
2fb8ea3ea0 | ||
|
1c6ada4af8 | ||
|
ca02bdcb61 | ||
|
90d93b364a | ||
|
6177afd5b1 | ||
|
5abec5bff0 | ||
|
ec792abbda | ||
|
6890da11ed | ||
|
1b31e80f25 | ||
|
d2a33bc210 | ||
|
3cfdf4c946 | ||
|
5a7f16b6f9 | ||
|
32e521fdc7 | ||
|
521f3d88d2 | ||
|
cf8522b1ef | ||
|
6cff47c6d8 | ||
|
366932e999 | ||
|
be2ded3b7e | ||
|
1809e38911 | ||
|
06ec539839 | ||
|
84c6a0039c | ||
|
5ff57258ec | ||
|
d4f6b040f3 | ||
|
1104b28758 | ||
|
881a9a6d27 | ||
|
48f37aa1c0 | ||
|
7f2ab05f97 | ||
|
de092f8f00 | ||
|
9e48fdc91c | ||
|
05dcb4e8d7 | ||
|
864e8910c4 | ||
|
efafc2d691 | ||
|
ed490fc202 | ||
|
ae6dbeb287 | ||
|
75fee76693 | ||
|
caffcaaef2 | ||
|
2b7624e622 | ||
|
ad501d4309 | ||
|
126fa1809e | ||
|
d44abbb48e | ||
|
40a49042e9 |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"parser": "babel-eslint",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2017,
|
"ecmaVersion": 2020,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"allowImportExportEverywhere": true
|
"allowImportExportEverywhere": true
|
||||||
},
|
},
|
||||||
@ -10,8 +10,12 @@
|
|||||||
"jasmine": true,
|
"jasmine": true,
|
||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"plugins": [],
|
"plugins": ["@typescript-eslint"],
|
||||||
"extends": ["eslint:recommended"],
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
"globals": {
|
"globals": {
|
||||||
"Uint8Array": true,
|
"Uint8Array": true,
|
||||||
"Promise": true,
|
"Promise": true,
|
||||||
|
28
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
Thanks for contributing to [Converse.js](https://conversejs.org)
|
||||||
|
|
||||||
|
## Support questions
|
||||||
|
|
||||||
|
The Github issue tracker is used for bug reports and feature requests, not for general tech support.
|
||||||
|
|
||||||
|
For support, you can join our [XMPP webchat](https://inverse.chat/#converse/room?jid=discuss@conference.conversejs.org).
|
||||||
|
Instead of the webchat, you can also open the room in your XMPP client, [click here](xmpp://discuss@conference.conversejs.org?join).
|
||||||
|
|
||||||
|
You can also ask questions on [StackOverflow](https://stackoverflow.com/questions/tagged/converse.js)
|
||||||
|
|
||||||
|
## Contributing Code
|
||||||
|
|
||||||
|
Please follow the usual Github workflow. Create a fork of this repository, make your changes and then submit a pull request.
|
||||||
|
|
||||||
|
### Before submitting a pull request
|
||||||
|
|
||||||
|
Please read the [style guide](https://conversejs.org/docs/html/style_guide.html) and make sure that your code follows it.
|
||||||
|
|
||||||
|
### Add tests for your bugfix or feature
|
||||||
|
|
||||||
|
Add a test for any bug fixed or feature added.
|
||||||
|
|
||||||
|
Tests can be found in various `./tests` folders in the Converse source code.
|
||||||
|
|
||||||
|
To run the tests, you can run `make check` on Linux and Mac, or `./node_modules/bin/karma start karma.conf` on Windows.
|
37
.github/CONTRIBUTING.rst
vendored
@ -1,37 +0,0 @@
|
|||||||
=======================
|
|
||||||
Contribution Guidelines
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Thanks for contributing to `Converse.js <https://conversejs.org>`_.
|
|
||||||
|
|
||||||
Support questions
|
|
||||||
=================
|
|
||||||
Please ask support and setup questions on the mailing list: conversejs@librelist.com
|
|
||||||
|
|
||||||
The issue tracker is only for bugs (i.e. issues) and feature requests.
|
|
||||||
|
|
||||||
Contributing Code
|
|
||||||
=================
|
|
||||||
Please follow the usual github workflow. Create your own local fork of this repository,
|
|
||||||
make your changes and then submit a pull request.
|
|
||||||
|
|
||||||
Before submitting a pull request
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Please read the `style guide <https://conversejs.org/docs/html/style_guide.html>`_ and make sure that your code follows it.
|
|
||||||
|
|
||||||
Add tests for your bugfix or feature
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Add a test for any bug fixed or feature added. We use Jasmine
|
|
||||||
for testing.
|
|
||||||
|
|
||||||
Take a look at `tests.html <https://github.com/jcbrand/converse.js/blob/master/tests.html>`_
|
|
||||||
and the `spec files <https://github.com/jcbrand/converse.js/blob/master/tests.html>`_
|
|
||||||
to see how tests are implemented.
|
|
||||||
|
|
||||||
Check that the tests pass
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Check that all tests complete sucessfully.
|
|
||||||
|
|
||||||
Run ``make check`` in your terminal or open `tests.html <https://github.com/jcbrand/converse.js/blob/master/tests.html>`_
|
|
||||||
in your browser.
|
|
2
.github/codeql-config.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
paths-ignore:
|
||||||
|
- '**/tests/*.js'
|
43
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
schedule:
|
||||||
|
- cron: "11 18 * * 6"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ javascript ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
config-file: ./.github/codeql-config.yml
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
queries: +security-and-quality
|
||||||
|
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
if: ${{ matrix.language == 'javascript' }}
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
34
.github/workflows/karma-tests.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||||
|
|
||||||
|
name: CI Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DISPLAY: :99.0
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [18.x]
|
||||||
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
- name: Run Karma tests
|
||||||
|
uses: GabrielBB/xvfb-action@v1
|
||||||
|
with:
|
||||||
|
run: make check ARGS=--single-run
|
3
.gitignore
vendored
@ -1,6 +1,9 @@
|
|||||||
# Distribution directory
|
# Distribution directory
|
||||||
dist
|
dist
|
||||||
|
|
||||||
|
# conversejs/media repo checkout
|
||||||
|
media
|
||||||
|
|
||||||
# Editor fluff
|
# Editor fluff
|
||||||
*~
|
*~
|
||||||
.sw?
|
.sw?
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"arrowParens": "avoid",
|
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"quoteProps": "preserve",
|
"quoteProps": "preserve",
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
|
15
.travis.yml
@ -1,15 +0,0 @@
|
|||||||
dist: bionic
|
|
||||||
language: node_js
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
addons:
|
|
||||||
chrome: stable
|
|
||||||
node_js:
|
|
||||||
- "14"
|
|
||||||
install: make node_modules
|
|
||||||
services:
|
|
||||||
- xvfb
|
|
||||||
before_script:
|
|
||||||
- export DISPLAY=:99.0
|
|
||||||
script: make check ARGS=--single-run
|
|
36541
3rdparty/libsignal-protocol.js
vendored
Normal file
1
3rdparty/libsignal-protocol.min.js
vendored
Normal file
168
CHANGES.md
@ -1,5 +1,169 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 10.1.6 (2023-08-31)
|
||||||
|
|
||||||
|
- #3246: Badge color not responsive to dark theme
|
||||||
|
- Fix a GIF rendering bug that causes a memory overflow
|
||||||
|
|
||||||
|
## 10.1.5 (2023-06-29)
|
||||||
|
|
||||||
|
- #3209: Fix error when importing the `converse` global with bootstrap modal API
|
||||||
|
- #3207: `.po` translation files weren't included in previous release
|
||||||
|
- Updated Galician and Portuguese translations
|
||||||
|
|
||||||
|
## 10.1.4 (2023-06-25)
|
||||||
|
|
||||||
|
- Fix `dist` directory not included in NPM package
|
||||||
|
|
||||||
|
## 10.1.3 (2023-06-23)
|
||||||
|
|
||||||
|
- Add the ability to set roles and affiliations via the MUC occupant modal
|
||||||
|
- Fix `isOnlyEmojis is not a function` when using only `@converse/headless`
|
||||||
|
- Fix `autojoin` checkbox state in MUC bookmark form
|
||||||
|
- Remove call to `api.confirm` in `@converse/headless`
|
||||||
|
- Generate TypeScript declaration files into `dist/types`
|
||||||
|
- Removed documentation about the no longer implemented `fullname` option.
|
||||||
|
- Updated translations
|
||||||
|
- #3123: Contacts do not show up online until chat is opened with them.
|
||||||
|
- #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode
|
||||||
|
- #3165: Use configured nickname in profile view in the control box
|
||||||
|
|
||||||
|
- New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#stanza-timeout)
|
||||||
|
|
||||||
|
## 10.1.2 (2023-02-17)
|
||||||
|
|
||||||
|
- #1490: Busy-loop when fetching registration form fails
|
||||||
|
- #1556: Can't switch to registration form afrer logout
|
||||||
|
- #3137: Various UI/UX bugfixes regarding the registration form
|
||||||
|
- XEP-0437: Room Activity Indicators (RAI) optimizations
|
||||||
|
|
||||||
|
## 10.1.1 (2023-02-15)
|
||||||
|
|
||||||
|
- #1851: Sort open groupchats alphabetically
|
||||||
|
- #2240: Ad-Hoc command result form not shown
|
||||||
|
- #3128: Second bookmarked room shows info of the first one
|
||||||
|
- Bugfix. Uyghur translations weren't loading
|
||||||
|
|
||||||
|
## 10.1.0 (2023-01-07)
|
||||||
|
|
||||||
|
- #326: Add the ability to reset your password
|
||||||
|
- #2759: Don't automatically log in again if the user manually logged out
|
||||||
|
- #2816: Chat highlight behaves odd
|
||||||
|
- #2925: File upload is not always enabled
|
||||||
|
- #3001: Add option to save SCRAM details and to use them to stay logged in upon reload
|
||||||
|
- Add a "Add to Contacts" button in MUC occupant modals
|
||||||
|
- Updated translations and add support for Uyghur
|
||||||
|
|
||||||
|
- New config option [reuse_scram_keys](https://conversejs.org/docs/html/configuration.html#reuse-scram-keys)
|
||||||
|
|
||||||
|
## 10.0.0 (2022-10-30)
|
||||||
|
|
||||||
|
- Update to Strophe.js 1.6.0 which adds support for SCRAM-SHA-256 and SCRAM-SHA-512
|
||||||
|
- Don't automatically convert OpenStreetMap URLs into `geo:` URIs in sent messages
|
||||||
|
- Remove the `allow_chat_pending_contacts` config option.
|
||||||
|
- Show roster contacts with `subscription` set to `none`
|
||||||
|
- Remove the `converse-carbons` plugin and make carbons part of the `converse-chat` plugin.
|
||||||
|
- Remove the `message_carbons` configuration setting. Carbons are now always enabled.
|
||||||
|
- Move the `converse-oauth` plugin to the [community-plugins](https://github.com/conversejs/community-plugins)
|
||||||
|
- Don't apply message corrections when the MUC occupant-id doesn't match.
|
||||||
|
- Update `nick` attribute on ChatRoom when user nickname changes
|
||||||
|
- Restrict editing of MUC messages to ones with the same XEP-0421 occupant ID
|
||||||
|
- Remove unfurls for links removed in a subsequent message correction
|
||||||
|
- Bookmarks now appear in a modal and the `hide_open_bookmarks` config setting has been removed.
|
||||||
|
- #1004: Stop using fonts to render icons and use SVG instead
|
||||||
|
- #2797: Weird unicode characters rendering outside of line-height
|
||||||
|
- #2870: Fix for multiple URLs to be linkified when sent together in chat and adds a test for this.
|
||||||
|
- #2879: Quotes, lines not aligned to the first line
|
||||||
|
- #2925: Fix missing disco-items in browser storage.
|
||||||
|
- #2936: Fix documentation about enable_smacks option, which is true by default.
|
||||||
|
- #2939: Data forms with a field named "username" are not displayed
|
||||||
|
- #3005: Fix MUC messages with a fallback body not rendering.
|
||||||
|
- #3007: Fix links becoming text when a message is edited
|
||||||
|
- #3018: Fix MUC icons not functioning.
|
||||||
|
- #3028: Fix encrypted media from Conversations/Quicksy not properly decrypting
|
||||||
|
|
||||||
|
|
||||||
|
## 9.1.1 (2022-05-05)
|
||||||
|
|
||||||
|
- GIFs don't render inside unfurls and cause a TypeError
|
||||||
|
- Improve how the `muc_domain` setting is populated via service discovery
|
||||||
|
- Remove local (non-requesting) contacts not returned from a full roster response
|
||||||
|
- Improve performance by looking up VCards via map instead of traversing an array
|
||||||
|
- Fix infinite loop when receiving service discovery entities with a circular dependency
|
||||||
|
- #1130: Display the room’s vCard-temp avatar
|
||||||
|
- #2746: Always reply to all iqs, even those not understood
|
||||||
|
- #2794: Some display problems with mobile view mode
|
||||||
|
- #2868: Selected emoji is inserted into all open chat boxes
|
||||||
|
|
||||||
|
Remove the very old and largely unused config option `allow_muc`.
|
||||||
|
If you don't want MUC support, you can add `'converse-muc'` to the
|
||||||
|
`blacklisted_plugins` setting.
|
||||||
|
|
||||||
|
## 9.1.0 (2022-04-02)
|
||||||
|
|
||||||
|
- Updated translations: af, ar, es, eu, fr, gl, he, lt
|
||||||
|
- Increased stanza timeout from 10 to 20 seconds
|
||||||
|
- Replace various font icons with SVG icons
|
||||||
|
- Fix bug where MUC config wasn't persisted across page loads
|
||||||
|
- Add support for calling the IndexedDB `getAll` method to speed up fetching models from storage.
|
||||||
|
- #1761: Add a new dark theme based on the [Dracula](https://draculatheme.com/) theme
|
||||||
|
- #2557: Allow OMEMO encrypted messages to be edited
|
||||||
|
- #2627: Spoiler toggles only after switching to another tab and back
|
||||||
|
- #2733: Fix OMEMO race condition related to automatic reconnection and SMACKS
|
||||||
|
- #2733: Wait for decrypted/parsed message before queuing to UI
|
||||||
|
- #2751: Media not rendered when Converse runs in a browser extension
|
||||||
|
- #2754: Navigation keys doesn't work anymore into chat input message
|
||||||
|
- #2781: Flashing error when connecting to a room
|
||||||
|
- #2786: Fix webpack configuration not working on Windows OS
|
||||||
|
- #2788: `TypeError` when trying to use `@converse/headless`
|
||||||
|
- #2789: Implement new hook `parseMessageForCommands` for plugins to add custom commands
|
||||||
|
- #2814: Links are mangled on open/copy
|
||||||
|
- #2822: Singleton doesn't work in overlayed view mode
|
||||||
|
|
||||||
|
- New config option [show_connection_url_input](https://conversejs.org/docs/html/configuration.html#show-connection-url-input)
|
||||||
|
|
||||||
|
## 9.0.0 (2021-11-26)
|
||||||
|
|
||||||
|
- Compress new avatar images before uploading them
|
||||||
|
- Add initial support for XEP-0421 occupant Ids.
|
||||||
|
- Use more specific types for form fields based on XEP-0122
|
||||||
|
- Fix trimming of chats in overlayed view mode
|
||||||
|
- OMEMO bugfix: Always create device session based on real JID.
|
||||||
|
- If `auto_register_muc_nickname` is set, make sure to register when the user changes current nick.
|
||||||
|
- #1322: Display occupants’ avatars in the occupants list
|
||||||
|
- #1419: Clicking on avatar should show bigger version
|
||||||
|
- #1426: Don't fetch member list if not affiliated
|
||||||
|
- #2423: Could not find dependency "converse-controlbox" for plugin "converse-muc"
|
||||||
|
- #2640: Add `beforeFetchLoginCredentials` hook
|
||||||
|
- #2647: Singleton mode doesn't work
|
||||||
|
- #2650: Clicking on occupant in sidebar should add nickname to textarea
|
||||||
|
- #2683: Show error messages that don't refer to specific chat messages
|
||||||
|
- #2704: Send button doesn't work in a multi-user chat
|
||||||
|
- #2715: Singleton + fullscreen mode styling issue
|
||||||
|
- #2718: Message is not displayed if it contains an invalid URL
|
||||||
|
- #2725: Send new presence status to all connected MUCs
|
||||||
|
- #2728: Not sending headers with upload request
|
||||||
|
- #2733: OMEMO Messages received while client closed not decrypted
|
||||||
|
|
||||||
|
- Emit a `change` event when a configuration setting changes
|
||||||
|
- 3 New configuration settings:
|
||||||
|
- [render_media](https://conversejs.org/docs/html/configuration.html#render-media)
|
||||||
|
- [allowed_audio_domains](https://conversejs.org/docs/html/configuration.html#allowed-audio-domains)
|
||||||
|
- [allowed_video_domains](https://conversejs.org/docs/html/configuration.html#allowed-video-domains)
|
||||||
|
- [allowed_image_domains](https://conversejs.org/docs/html/configuration.html#allowed-image-domains)
|
||||||
|
|
||||||
|
Three config settings have been obsoleted:
|
||||||
|
- embed_audio
|
||||||
|
- embed_video
|
||||||
|
- show_images_inline
|
||||||
|
- muc_show_ogp_unfurls
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
Configuration settings are no longer available on the `_converse` object.
|
||||||
|
Instead, use `api.settings.get` and `api.settings.set`.
|
||||||
|
|
||||||
|
|
||||||
## 8.0.1 (2021-09-09)
|
## 8.0.1 (2021-09-09)
|
||||||
|
|
||||||
- #2326: 404 for asset /logo/conversejs-filled.svg
|
- #2326: 404 for asset /logo/conversejs-filled.svg
|
||||||
@ -23,7 +187,7 @@
|
|||||||
- #2348: `auto_join_room` not showing the room in `fullscreen` `view_mode`.
|
- #2348: `auto_join_room` not showing the room in `fullscreen` `view_mode`.
|
||||||
- #2400: Fixes infinite loop bug when appending .png to allowed image urls
|
- #2400: Fixes infinite loop bug when appending .png to allowed image urls
|
||||||
- #2409: Integrate App Badging API for unread messages
|
- #2409: Integrate App Badging API for unread messages
|
||||||
- #2464: New configuration setting [allow-url-history-change](https://conversejs.org/docs/html/configuration.html#allow-url-history-change)
|
- #2464: New configuration setting [allow_url_history_change](https://conversejs.org/docs/html/configuration.html#allow-url-history-change)
|
||||||
- #2497: Bugfix /nick command is not working
|
- #2497: Bugfix /nick command is not working
|
||||||
- Add a Description Of A Project (DOAP) file
|
- Add a Description Of A Project (DOAP) file
|
||||||
- Add ability to deregister nickname when closing a MUC by setting `auto_register_muc_nickname` to `'unregister'`.
|
- Add ability to deregister nickname when closing a MUC by setting `auto_register_muc_nickname` to `'unregister'`.
|
||||||
@ -193,7 +357,7 @@ Soon we'll deprecate the latter, so prepare now.
|
|||||||
|
|
||||||
## 6.0.0 (2020-01-09)
|
## 6.0.0 (2020-01-09)
|
||||||
|
|
||||||
- [enable_smacks](https://conversejs.org/docs/html/configuration.html#enable-smacks) is not set to `true` by default.
|
- [enable_smacks](https://conversejs.org/docs/html/configuration.html#enable-smacks) is now set to `true` by default.
|
||||||
- Refactor some presence and status handling code from `converse-core` into `@converse/headless/converse-status`.
|
- Refactor some presence and status handling code from `converse-core` into `@converse/headless/converse-status`.
|
||||||
- It's now possible to navigate the emoji-picker via the keyboard arrow keys.
|
- It's now possible to navigate the emoji-picker via the keyboard arrow keys.
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* An XMPP chat client that runs in the browser.
|
* An XMPP chat client that runs in the browser.
|
||||||
*
|
*
|
||||||
* Version: 8.0.1
|
* Version: 10.1.6
|
||||||
*
|
*
|
||||||
* Copyright: JC Brand 2013-2018
|
* Copyright: JC Brand 2013-2018
|
||||||
* Except for 3rd party dependencies.
|
* Except for 3rd party dependencies.
|
||||||
|
99
Makefile
@ -1,17 +1,13 @@
|
|||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line.
|
||||||
BABEL ?= node_modules/.bin/babel
|
|
||||||
BOOTSTRAP = ./node_modules/
|
BOOTSTRAP = ./node_modules/
|
||||||
BUILDDIR = ./docs
|
BUILDDIR = ./docs
|
||||||
KARMA ?= ./node_modules/.bin/karma
|
KARMA ?= ./node_modules/.bin/karma
|
||||||
CHROMIUM ?= ./node_modules/.bin/run-headless-chromium
|
|
||||||
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
|
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
|
||||||
ESLINT ?= ./node_modules/.bin/eslint
|
|
||||||
HTTPSERVE ?= ./node_modules/.bin/http-server
|
HTTPSERVE ?= ./node_modules/.bin/http-server
|
||||||
HTTPSERVE_PORT ?= 8000
|
HTTPSERVE_PORT ?= 8000
|
||||||
INKSCAPE ?= inkscape
|
INKSCAPE ?= inkscape
|
||||||
INSTALL ?= install
|
INSTALL ?= install
|
||||||
JSDOC ?= ./node_modules/.bin/jsdoc
|
JSDOC ?= ./node_modules/.bin/jsdoc
|
||||||
LERNA ?= ./node_modules/.bin/lerna
|
|
||||||
OXIPNG ?= oxipng
|
OXIPNG ?= oxipng
|
||||||
PAPER =
|
PAPER =
|
||||||
RJS ?= ./node_modules/.bin/r.js
|
RJS ?= ./node_modules/.bin/r.js
|
||||||
@ -62,13 +58,14 @@ serve: node_modules dist
|
|||||||
serve_bg: node_modules
|
serve_bg: node_modules
|
||||||
$(HTTPSERVE) -p $(HTTPSERVE_PORT) -c-1 -s &
|
$(HTTPSERVE) -p $(HTTPSERVE_PORT) -c-1 -s &
|
||||||
|
|
||||||
|
certs:
|
||||||
|
mkdir certs
|
||||||
|
cd certs && openssl req -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out chat.example.org.crt -keyout chat.example.org.key
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Translation machinery
|
## Translation machinery
|
||||||
|
|
||||||
dist/converse-no-dependencies.js: src webpack.common.js webpack.nodeps.js @converse/headless node_modules
|
GETTEXT = $(XGETTEXT) --from-code=UTF-8 --language=JavaScript --keyword=__ --keyword=___ --keyword=i18n_ --force-po --output=src/i18n/converse.pot --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=10.1.6 dist/converse-no-dependencies.js -c
|
||||||
npm run nodeps
|
|
||||||
|
|
||||||
GETTEXT = $(XGETTEXT) --from-code=UTF-8 --language=JavaScript --keyword=__ --keyword=___ --keyword=i18n_ --force-po --output=src/i18n/converse.pot --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=8.0.1 dist/converse-no-dependencies.js -c
|
|
||||||
|
|
||||||
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
||||||
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
||||||
@ -85,9 +82,9 @@ po:
|
|||||||
########################################################################
|
########################################################################
|
||||||
## Release management
|
## Release management
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: version
|
||||||
release:
|
version:
|
||||||
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/core.js
|
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/shared/constants.js
|
||||||
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
|
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
|
||||||
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
|
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
|
||||||
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
|
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
|
||||||
@ -102,19 +99,33 @@ release:
|
|||||||
make pot
|
make pot
|
||||||
make po
|
make po
|
||||||
make dist
|
make dist
|
||||||
npm pack
|
|
||||||
cd src/headless && npm pack
|
release-checkout:
|
||||||
|
git clone git@github.com:conversejs/converse.js.git --depth 1 --branch $(BRANCH) release-$(BRANCH)
|
||||||
|
cd release-$(BRANCH) && make dist
|
||||||
|
|
||||||
|
.PHONY: publish
|
||||||
|
publish:
|
||||||
|
make release-checkout
|
||||||
|
cd release-$(BRANCH) && npm pack && npm publish
|
||||||
|
cd release-$(BRANCH)/src/headless && npm pack && npm publish
|
||||||
|
find ./release-$(BRANCH)/ -name "converse.js-*.tgz" -exec mv {} . \;
|
||||||
|
find ./release-$(BRANCH)/src/headless -name "converse-headless-*.tgz" -exec mv {} . \;
|
||||||
|
rm -rf release-$(BRANCH)
|
||||||
|
|
||||||
.PHONY: postrelease
|
.PHONY: postrelease
|
||||||
postrelease:
|
postrelease:
|
||||||
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/core.js
|
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/shared/constants.js
|
||||||
|
|
||||||
|
.PHONY: deploy
|
||||||
|
deploy:
|
||||||
|
git clone --branch v$(VERSION) git@github.com:conversejs/converse.js.git --depth 1 $(VERSION)
|
||||||
|
cd $(VERSION) && make node && ASSET_PATH=https://cdn.conversejs.org/$(VERSION)/dist/ make dist && make doc
|
||||||
|
cd .. && git pull && make node && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Install dependencies
|
## Install dependencies
|
||||||
|
|
||||||
$(LERNA):
|
|
||||||
npm install lerna
|
|
||||||
|
|
||||||
${NVM_DIR}/nvm.sh:
|
${NVM_DIR}/nvm.sh:
|
||||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
||||||
source ~/.bashrc
|
source ~/.bashrc
|
||||||
@ -126,12 +137,9 @@ nvm: ${NVM_DIR}/nvm.sh
|
|||||||
node: .nvmrc
|
node: .nvmrc
|
||||||
. $(HOME)/.nvm/nvm.sh && nvm install
|
. $(HOME)/.nvm/nvm.sh && nvm install
|
||||||
|
|
||||||
package-lock.json: package.json
|
node_modules: package.json src/headless/package.json
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
node_modules: $(LERNA) package.json package-lock.json src/headless/package.json src/headless/package-lock.json
|
|
||||||
npm run lerna
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
npm run clean
|
npm run clean
|
||||||
@ -148,11 +156,14 @@ devserver: node_modules
|
|||||||
########################################################################
|
########################################################################
|
||||||
## Builds
|
## Builds
|
||||||
|
|
||||||
|
dist/converse-no-dependencies.js: src webpack/webpack.common.js webpack/webpack.nodeps.js @converse/headless node_modules
|
||||||
|
npm run nodeps
|
||||||
|
|
||||||
dist/converse.js:: node_modules
|
dist/converse.js:: node_modules
|
||||||
npm run dev
|
npm run build
|
||||||
|
|
||||||
dist/converse.css:: node_modules
|
dist/converse.css:: node_modules
|
||||||
npm run dev
|
npm run build
|
||||||
|
|
||||||
dist/website.css:: node_modules src/shared/styles/website.scss
|
dist/website.css:: node_modules src/shared/styles/website.scss
|
||||||
$(SASS) --load-path=$(BOOTSTRAP) src/shared/styles/website.scss $@
|
$(SASS) --load-path=$(BOOTSTRAP) src/shared/styles/website.scss $@
|
||||||
@ -186,12 +197,24 @@ logo/conversejs-filled%.png:: logo/conversejs-filled.svg
|
|||||||
|
|
||||||
@converse/headless: src/headless
|
@converse/headless: src/headless
|
||||||
|
|
||||||
src/headless/dist/converse-headless.min.js: src webpack.common.js node_modules @converse/headless
|
src/headless/dist/converse-headless.js: src webpack/webpack.common.js node_modules @converse/headless
|
||||||
|
npm run headless-dev
|
||||||
|
|
||||||
|
src/headless/dist/converse-headless.min.js: src webpack/webpack.common.js node_modules @converse/headless
|
||||||
npm run headless
|
npm run headless
|
||||||
|
|
||||||
dist:: node_modules src/* | dist/converse.js dist/converse.css dist/website.css dist/website.min.css
|
dist:: node_modules src/* | dist/website.css dist/website.min.css
|
||||||
npm run prod
|
|
||||||
npm run headless
|
npm run headless
|
||||||
|
# Ideally this should just be `npm run build`.
|
||||||
|
# The additional steps are necessary to properly generate JSON chunk files
|
||||||
|
# from the .po files. The nodeps config uses preset-env with IE11.
|
||||||
|
# Somehow this is necessary.
|
||||||
|
npm run nodeps
|
||||||
|
$(eval TMPD := $(shell mktemp -d))
|
||||||
|
mv dist/locales $(TMPD) && \
|
||||||
|
npm run build && \
|
||||||
|
mv $(TMPD)/locales/*-po.js dist/locales/ && \
|
||||||
|
rm -rf $(TMPD)
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:: dist
|
install:: dist
|
||||||
@ -200,23 +223,24 @@ install:: dist
|
|||||||
cdn:: node_modules
|
cdn:: node_modules
|
||||||
npm run cdn
|
npm run cdn
|
||||||
|
|
||||||
|
.PHONY: types
|
||||||
|
types:: node_modules
|
||||||
|
npm run types
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
.PHONY: eslint
|
.PHONY: eslint
|
||||||
eslint: node_modules
|
eslint: node_modules
|
||||||
$(ESLINT) src/*.js
|
npm run lint
|
||||||
$(ESLINT) src/utils/*.js
|
|
||||||
$(ESLINT) src/headless/*.js
|
|
||||||
$(ESLINT) src/headless/utils/*.js
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: eslint | dist/converse.js dist/converse.css
|
check: eslint | dist/converse.js dist/converse.css
|
||||||
$(KARMA) start karma.conf.js $(ARGS)
|
npm run test -- $(ARGS)
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
$(KARMA) start karma.conf.js $(ARGS)
|
npm run test -- $(ARGS)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Documentation
|
## Documentation
|
||||||
@ -224,13 +248,10 @@ test:
|
|||||||
./bin/activate:
|
./bin/activate:
|
||||||
python3 -m venv .
|
python3 -m venv .
|
||||||
|
|
||||||
.installed.cfg: requirements.txt buildout.cfg
|
.PHONY: docsdev
|
||||||
|
docsdev: ./bin/activate requirements.txt
|
||||||
|
./bin/pip install --upgrade pip==21.3.1
|
||||||
./bin/pip install -r requirements.txt
|
./bin/pip install -r requirements.txt
|
||||||
./bin/pip install --upgrade pip==19.2.1
|
|
||||||
./bin/pip install --upgrade setuptools==41.0.1
|
|
||||||
./bin/buildout -v
|
|
||||||
|
|
||||||
docsdev: ./bin/activate .installed.cfg
|
|
||||||
|
|
||||||
.PHONY: html
|
.PHONY: html
|
||||||
html: doc
|
html: doc
|
||||||
@ -245,4 +266,4 @@ doc: node_modules docsdev apidoc
|
|||||||
|
|
||||||
PHONY: apidoc
|
PHONY: apidoc
|
||||||
apidoc:
|
apidoc:
|
||||||
$(JSDOC) --private --readme docs/source/jsdoc_intro.md -c docs/source/conf.json -d docs/html/api src/templates/*.js src/*.js src/**/*.js src/headless/**/*.js src/shared/**/*.js
|
find ./src -type d -name node_modules -prune -false -o -name "*.js" | xargs $(JSDOC) --private --readme docs/source/jsdoc_intro.md -c docs/source/conf.json -d docs/html/api
|
||||||
|
17
README.chapril.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Construire une version Chapril de ConverseJS
|
||||||
|
|
||||||
|
```
|
||||||
|
cd .../conversejs
|
||||||
|
# La première fois, installer nvm (attention, ça va modifier le .bashrc, entre autres choses)
|
||||||
|
make nvm
|
||||||
|
|
||||||
|
export V=7.0.4
|
||||||
|
git rebase v${V?}
|
||||||
|
# [... Résoudre les conflits]
|
||||||
|
git checkout -b v${V?}-chapril
|
||||||
|
nvm install
|
||||||
|
make dist
|
||||||
|
# [... Tester les livrables présents dans dist/, et si tout est ok :]
|
||||||
|
make version VERSION=${V?}-chapril
|
||||||
|
rsync -av dist/ chapril-xmpp:/var/www/xmpp.chapril.org/public_html/dist-custom-chapril-${V?}/
|
||||||
|
```
|
37
README.md
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[![XMPP Chat](https://conference.conversejs.org/muc_badge/discuss@conference.conversejs.org)](https://inverse.chat/#converse/room?jid=discuss@conference.conversejs.org)
|
[![XMPP Chat](https://conference.conversejs.org/muc_badge/discuss@conference.conversejs.org)](https://inverse.chat/#converse/room?jid=discuss@conference.conversejs.org)
|
||||||
[![Travis](https://api.travis-ci.com/conversejs/converse.js.png?branch=master)](https://travis-ci.com/conversejs/converse.js)
|
[![CI Tests](https://github.com/conversejs/converse.js/actions/workflows/karma-tests.yml/badge.svg)](https://github.com/conversejs/converse.js/actions/workflows/karma-tests.yml)
|
||||||
[![Bountysource bounties](https://img.shields.io/bountysource/team/converse.js/activity.svg?maxAge=2592000)](https://www.bountysource.com/teams/converse.js/issues?tracker_ids=194169)
|
[![Bountysource bounties](https://img.shields.io/bountysource/team/converse.js/activity.svg?maxAge=2592000)](https://www.bountysource.com/teams/converse.js/issues?tracker_ids=194169)
|
||||||
[![Translation status](https://hosted.weblate.org/widgets/conversejs/-/svg-badge.svg)](https://hosted.weblate.org/engage/conversejs/?utm_source=widget)
|
[![Translation status](https://hosted.weblate.org/widgets/conversejs/-/svg-badge.svg)](https://hosted.weblate.org/engage/conversejs/?utm_source=widget)
|
||||||
|
|
||||||
@ -46,7 +46,11 @@ In overlay mode, Converse appears overlayed chats on top of the website.
|
|||||||
|
|
||||||
In fullpage mode, Converse behaves like a single-page app that covers the whole browser viewport.
|
In fullpage mode, Converse behaves like a single-page app that covers the whole browser viewport.
|
||||||
|
|
||||||
![Screenshot of Converse in fullpage mode](https://conversejs.org/screenshots/Converse-fullscreen.png)
|
![Screenshot of Converse 9.0.0 in fullpage mode](https://conversejs.org/screenshots/Converse-fullscreen.png)
|
||||||
|
|
||||||
|
### Dark mode
|
||||||
|
|
||||||
|
![Screenshot of Converse 9.1.0 with a dark theme](https://conversejs.org/screenshots/Converse-Dracula-Theme.png)
|
||||||
|
|
||||||
### Embedded
|
### Embedded
|
||||||
|
|
||||||
@ -114,14 +118,20 @@ In embedded mode, Converse can be embedded into an element in the DOM.
|
|||||||
|
|
||||||
## Integration into other servers and frameworks
|
## Integration into other servers and frameworks
|
||||||
|
|
||||||
|
### XMPP servers
|
||||||
|
|
||||||
|
- **[Openfire](https://www.igniterealtime.org/projects/openfire/index.jsp)**: [inverse.jar](https://www.igniterealtime.org/projects/openfire/plugins.jsp)
|
||||||
|
- **[Prosody](https://prosody.im/)**: [mod_conversejs](https://modules.prosody.im/mod_conversejs.html)
|
||||||
|
- **[Ejabberd](https://ejabberd.im/)**: [mod-conversejs](https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
- **[Alfresco](https://www.alfresco.com)**: [alfresco-js-chat-share](https://github.com/keensoft/alfresco-js-chat-share)
|
- **[Alfresco](https://www.alfresco.com)**: [alfresco-js-chat-share](https://github.com/keensoft/alfresco-js-chat-share)
|
||||||
- **[Django](https://www.djangoproject.com)**: [django-conversejs](https://pypi.python.org/pypi/django-conversejs) or [django-xmpp](https://github.com/fpytloun/django-xmpp)
|
- **[Django](https://www.djangoproject.com)**: [django-conversejs](https://pypi.python.org/pypi/django-conversejs) or [django-xmpp](https://github.com/fpytloun/django-xmpp)
|
||||||
- **[Elgg](https://elgg.org)**: [plugin](https://elgg.org/plugins/2997196)
|
- **[Elgg](https://elgg.org)**: [plugin](https://elgg.org/plugins/2997196)
|
||||||
- **[Friendica](https://friendi.ca)**: [converse](https://github.com/friendica/friendica-addons/tree/master/xmpp/converse)
|
- **[Friendica](https://friendi.ca)**: [converse](https://github.com/friendica/friendica-addons/tree/master/xmpp/converse)
|
||||||
- **[Openfire](https://www.igniterealtime.org/projects/openfire/index.jsp)**: [inverse.jar](https://www.igniterealtime.org/projects/openfire/plugins.jsp)
|
|
||||||
- **[Patternslib](http://patternslib.com)**: [patterns.converse](https://github.com/jcbrand/patterns.converse)
|
- **[Patternslib](http://patternslib.com)**: [patterns.converse](https://github.com/jcbrand/patterns.converse)
|
||||||
- **[Plone](https://plone.com)**: [collective.converse](https://github.com/collective/collective.converse)
|
- **[Plone](https://plone.com)**: [collective.converse](https://github.com/collective/collective.converse)
|
||||||
- **[Prosody](https://prosody.im/)**: [mod_conversejs](https://modules.prosody.im/mod_conversejs.html)
|
|
||||||
- **[Pàdé](https://www.igniterealtime.org/projects/pade/index.jsp)**: [Pàdé](https://www.igniterealtime.org/projects/pade/index.jsp)
|
- **[Pàdé](https://www.igniterealtime.org/projects/pade/index.jsp)**: [Pàdé](https://www.igniterealtime.org/projects/pade/index.jsp)
|
||||||
- **[Roundcube](https://roundcube.net)**: [roundcube-converse.js-xmpp-plugin](https://github.com/devurandom/roundcube-converse.js-xmpp-plugin)
|
- **[Roundcube](https://roundcube.net)**: [roundcube-converse.js-xmpp-plugin](https://github.com/devurandom/roundcube-converse.js-xmpp-plugin)
|
||||||
- **[Ruby on Rails](https://rubyonrails.org)**: [conversejs-rails](https://github.com/mikemarsian/conversejs-rails)
|
- **[Ruby on Rails](https://rubyonrails.org)**: [conversejs-rails](https://github.com/mikemarsian/conversejs-rails)
|
||||||
@ -156,14 +166,19 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
|
|||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
|
<img alt="BairesDev" src="https://raw.githubusercontent.com/conversejs/media/main/logos/bairesdev-primary.png" width="200">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
|
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img alt="Blokt Crypto & Privacy" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/blokt.png" width="200">
|
<img alt="Blokt Crypto & Privacy" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/blokt.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener">
|
<a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img alt="Codefirst" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/codefirst.png" width="200">
|
<img alt="Prime Sound" src="https://raw.githubusercontent.com/conversejs/media/main/logos/primesound.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -171,13 +186,3 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
|
|||||||
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
<a href="https://www.pluckeye.net/?utm_source=conversejs" target="_blank" rel="noopener">
|
|
||||||
<img alt="Pluckeye" src="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/pluckeye.svg" width="200">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener">
|
|
||||||
<img alt="Original Energie" src="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/originalenergie-black.png" width="200">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
26
RELEASE.md
@ -1,20 +1,18 @@
|
|||||||
# Release checklist
|
# Release checklist
|
||||||
|
|
||||||
1. Check that weblate translations are all merged in
|
1. Merge weblate translations: https://hosted.weblate.org/projects/conversejs/translations/#repository
|
||||||
2. Run `make check` to check that all tests pass.
|
2. Run `make check` to check that all tests pass.
|
||||||
3. Run `make release VERSION=8.0.1`
|
3. Run `make version VERSION=10.1.6`
|
||||||
4. Do a `git diff` to check if things look sane.
|
4. Do a `git diff` to check if things look sane.
|
||||||
5. Do a quick manual test with the `dist` files (via `index.html`)
|
5. Do a quick manual test with the `dist` files (via `index.html`)
|
||||||
6. `git commit -am "Release 8.0.1"`
|
6. `git commit -am "Release 10.1.6"`
|
||||||
7. `git tag -s v8.0.1 -m "Release 8.0.1"`
|
7. `git tag -s v10.1.6 -m "Release 10.1.6"`
|
||||||
8. Run `git push && git push origin v8.0.1`
|
8. `git push && git push origin v10.1.6`
|
||||||
9. Update https://conversejs.org
|
9. `make publish BRANCH=v10.1.6`
|
||||||
* `cd /home/conversejs/converse.js`
|
|
||||||
* `git clone --branch v8.0.1 git@github.com:conversejs/converse.js.git 8.0.1`
|
|
||||||
* `cd 8.0.1 && nvm install && ASSET_PATH=https://cdn.conversejs.org/8.0.1/dist/ make dist && make doc`
|
|
||||||
* `cd .. && nvm install && git pull && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc`
|
|
||||||
10. Update release page on Github
|
10. Update release page on Github
|
||||||
11. Run `npm pack && cd src/headless && npm pack` to generate tarballs. Then upload them to the Github release page
|
* Upload tar files
|
||||||
12. Run `npm publish && cd src/headless/ && npm publish`
|
11. Update https://conversejs.org
|
||||||
13. Update the repository on weblate
|
* `cd /home/conversejs/converse.js`
|
||||||
14. Decide on next release number and run `make postrelease VERSION=8.0.2`
|
* `make deploy VERSION=10.1.6`
|
||||||
|
12. Update the repository on weblate
|
||||||
|
13. Decide on next release number and run `make postrelease VERSION=10.1.7`
|
||||||
|
10
babel.config.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["@babel/preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"browsers": [">1%", "not ie 11", "not op_mini all", "not dead"]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
15
buildout.cfg
@ -1,15 +0,0 @@
|
|||||||
[buildout]
|
|
||||||
parts =
|
|
||||||
sphinx
|
|
||||||
|
|
||||||
versions = versions
|
|
||||||
|
|
||||||
[sphinx]
|
|
||||||
recipe = zc.recipe.egg
|
|
||||||
eggs =
|
|
||||||
Sphinx
|
|
||||||
sphinx-bootstrap-theme
|
|
||||||
|
|
||||||
[versions]
|
|
||||||
Sphinx = 2.1.2
|
|
||||||
docutils = 0.15.2
|
|
@ -1,23 +0,0 @@
|
|||||||
# How to use saved Chrome/Chromium logs to replay events
|
|
||||||
|
|
||||||
**NOTE**: This feature is very experimental and in many cases doesn't work
|
|
||||||
without data massaging and ugly hacks.
|
|
||||||
|
|
||||||
It's possible to save the log output from Chrome/Chromium (I haven't tried this
|
|
||||||
yet with any other browser) and then to replay that log output in the browser.
|
|
||||||
|
|
||||||
This can be a very helpful technique to track down bugs.
|
|
||||||
|
|
||||||
To do this, follow the following steps:
|
|
||||||
|
|
||||||
1. Save the log file (right click and then click "Save as" in the browser's console).
|
|
||||||
2. Rename the log file, making sure it ends in `.html`
|
|
||||||
3. Move the log file to the `converse-logs` directory in the converse.js repo.
|
|
||||||
4. Add `<log>` to the top of the log file and `</log>` to the bottom of the log file.
|
|
||||||
5. In `converse-logs/converse-logs.js`, add a new entry for the log file (don't
|
|
||||||
include the `.html` part of the file name.
|
|
||||||
6. Make sure that `spec/transcripts` is "required"-ed in `tests/main.js`
|
|
||||||
6. Open `tests.html` in your browser.
|
|
||||||
|
|
||||||
Your logs will run first, and then all the other tests will run afterwards.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
define("transcripts", [
|
|
||||||
"tpl!../../converse-logs/missing_messages",
|
|
||||||
], function () {
|
|
||||||
return arguments;
|
|
||||||
});
|
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<rdf:RDF xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xmpp="https://linkmauve.fr/ns/xmpp-doap#" xml:lang="en">
|
<rdf:RDF xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xmpp="https://linkmauve.fr/ns/xmpp-doap#" xmlns:schema="https://schema.org/" xml:lang="en">
|
||||||
<Project xmlns="http://usefulinc.com/ns/doap#">
|
<Project xmlns="http://usefulinc.com/ns/doap#">
|
||||||
<name>Converse.js</name>
|
<name>Converse.js</name>
|
||||||
<shortdesc>Browser based XMPP chat client</shortdesc>
|
<shortdesc>Browser based XMPP chat client</shortdesc>
|
||||||
@ -9,6 +9,11 @@
|
|||||||
<support-forum rdf:resource="xmpp:discuss@conference.conversejs.org?join"/>
|
<support-forum rdf:resource="xmpp:discuss@conference.conversejs.org?join"/>
|
||||||
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
|
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
|
||||||
<programming-language>JavaScript</programming-language>
|
<programming-language>JavaScript</programming-language>
|
||||||
|
<os>Browser</os>
|
||||||
|
<os>Linux</os>
|
||||||
|
<os>macOS</os>
|
||||||
|
<os>Windows</os>
|
||||||
|
<schema:logo rdf:resource="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/conversejs-filled.svg"/>
|
||||||
<repository>
|
<repository>
|
||||||
<GitRepository>
|
<GitRepository>
|
||||||
<browse rdf:resource="https://github.com/conversejs/converse.js"/>
|
<browse rdf:resource="https://github.com/conversejs/converse.js"/>
|
||||||
@ -87,6 +92,13 @@
|
|||||||
<xmpp:note>advertises caps but no caching</xmpp:note>
|
<xmpp:note>advertises caps but no caching</xmpp:note>
|
||||||
</xmpp:SupportedXep>
|
</xmpp:SupportedXep>
|
||||||
</implements>
|
</implements>
|
||||||
|
<implements>
|
||||||
|
<xmpp:SupportedXep>
|
||||||
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0122.html"/>
|
||||||
|
<xmpp:status>partial</xmpp:status>
|
||||||
|
<xmpp:note>basic string field sub-type usage</xmpp:note>
|
||||||
|
</xmpp:SupportedXep>
|
||||||
|
</implements>
|
||||||
<implements>
|
<implements>
|
||||||
<xmpp:SupportedXep>
|
<xmpp:SupportedXep>
|
||||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0124.html"/>
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0124.html"/>
|
||||||
@ -209,6 +221,11 @@
|
|||||||
<xmpp:since>4.0.0</xmpp:since>
|
<xmpp:since>4.0.0</xmpp:since>
|
||||||
</xmpp:SupportedXep>
|
</xmpp:SupportedXep>
|
||||||
</implements>
|
</implements>
|
||||||
|
<implements>
|
||||||
|
<xmpp:SupportedXep>
|
||||||
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0371.html"/>
|
||||||
|
</xmpp:SupportedXep>
|
||||||
|
</implements>
|
||||||
<implements>
|
<implements>
|
||||||
<xmpp:SupportedXep>
|
<xmpp:SupportedXep>
|
||||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0372.html"/>
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0372.html"/>
|
||||||
@ -238,6 +255,11 @@
|
|||||||
<xmpp:since>5.0.0</xmpp:since>
|
<xmpp:since>5.0.0</xmpp:since>
|
||||||
</xmpp:SupportedXep>
|
</xmpp:SupportedXep>
|
||||||
</implements>
|
</implements>
|
||||||
|
<implements>
|
||||||
|
<xmpp:SupportedXep>
|
||||||
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0421.html"/>
|
||||||
|
</xmpp:SupportedXep>
|
||||||
|
</implements>
|
||||||
<implements>
|
<implements>
|
||||||
<xmpp:SupportedXep>
|
<xmpp:SupportedXep>
|
||||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0422.html"/>
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0422.html"/>
|
||||||
|
@ -10,20 +10,20 @@
|
|||||||
<meta name="keywords" content="xmpp chat webchat converse.js Converse" />
|
<meta name="keywords" content="xmpp chat webchat converse.js Converse" />
|
||||||
|
|
||||||
<!-- These files are NOT needed when using converse.js in your own project. -->
|
<!-- These files are NOT needed when using converse.js in your own project. -->
|
||||||
<link rel="shortcut icon" type="image/ico" href="/dist/favicon.ico"/>
|
<link rel="shortcut icon" type="image/ico" href="../dist/favicon.ico"/>
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="/dist/website.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="../dist/website.min.css" />
|
||||||
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
<noscript><p><img src="https://stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
||||||
<script type="text/javascript" src="analytics.js"></script>
|
<script type="text/javascript" src="/analytics.js"></script>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="../manifest.json">
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="/dist/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="../dist/converse.min.css" />
|
||||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
<script src="/dist/converse.min.js"></script>
|
<script src="../dist/converse.min.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.converse-container {
|
.converse-container {
|
||||||
height: 55vh;
|
height: 50vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h1 class="brand-heading fade-in">
|
<h1 class="brand-heading fade-in" style="margin: 1.5em 0 0 0">
|
||||||
<svg class="converse-svg-logo"
|
<svg class="converse-svg-logo"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -72,7 +72,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<p class="intro-text">Embedded MUC chat demo</p>
|
<p class="intro-text">Embedded MUC chat demo</p>
|
||||||
<div class="converse-container">
|
<div class="converse-container">
|
||||||
<div id="conversejs"></div>
|
<converse-root></converse-root>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
9
dev.html
@ -28,19 +28,22 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
converse.initialize({
|
converse.initialize({
|
||||||
|
i18n: 'af',
|
||||||
|
theme: 'dracula',
|
||||||
auto_away: 300,
|
auto_away: 300,
|
||||||
enable_smacks: true,
|
enable_smacks: true,
|
||||||
loglevel: 'debug',
|
loglevel: 'debug',
|
||||||
|
reuse_scram_keys: true,
|
||||||
prune_messages_above: 100,
|
prune_messages_above: 100,
|
||||||
message_archiving: 'always',
|
message_archiving: 'always',
|
||||||
muc_respect_autojoin: true,
|
muc_respect_autojoin: true,
|
||||||
muc_show_logs_before_join: true,
|
muc_show_logs_before_join: true,
|
||||||
notify_all_room_messages: ['discuss@conference.conversejs.org'],
|
notify_all_room_messages: ['discuss@conference.conversejs.org'],
|
||||||
theme: 'concord',
|
|
||||||
view_mode: 'fullscreen',
|
view_mode: 'fullscreen',
|
||||||
websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
// websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
||||||
// websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
||||||
whitelisted_plugins: ['converse-debug'],
|
whitelisted_plugins: ['converse-debug'],
|
||||||
|
// connection_options: { worker: '/dist/shared-connection-worker.js' }
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -6,11 +6,9 @@
|
|||||||
<script src="scripts/prettify/prettify.js"></script>
|
<script src="scripts/prettify/prettify.js"></script>
|
||||||
<script src="scripts/prettify/lang-css.js"></script>
|
<script src="scripts/prettify/lang-css.js"></script>
|
||||||
<script src="./analytics.js"></script>
|
<script src="./analytics.js"></script>
|
||||||
<script src="/dist/converse.min.js"></script>
|
|
||||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/docs/source/_static/api.css">
|
<link type="text/css" rel="stylesheet" href="/docs/source/_static/api.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/dist/converse.min.css">
|
|
||||||
<link rel="shortcut icon" href="/images/favicon.ico"/>
|
<link rel="shortcut icon" href="/images/favicon.ico"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -61,6 +59,5 @@
|
|||||||
|
|
||||||
<script> prettyPrint(); </script>
|
<script> prettyPrint(); </script>
|
||||||
<script src="scripts/linenumber.js"> </script>
|
<script src="scripts/linenumber.js"> </script>
|
||||||
<script>converse.initialize({'bosh_service_url': 'https://conversejs.org/http-bind/'});</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -2,15 +2,12 @@
|
|||||||
{% extends "!layout.html" %}
|
{% extends "!layout.html" %}
|
||||||
|
|
||||||
{# Custom CSS overrides #}
|
{# Custom CSS overrides #}
|
||||||
{% set css_files = css_files + ['_static/style.css', "../../dist/converse.min.css"] %}
|
{% set css_files = css_files + ['_static/style.css'] %}
|
||||||
{% set script_files = script_files + ["../../dist/converse.min.js", "../../analytics.js"] %}
|
{% set script_files = script_files + ["../../analytics.js"] %}
|
||||||
|
|
||||||
{# Add some extra stuff before and use existing with 'super()' call. #}
|
{# Add some extra stuff before and use existing with 'super()' call. #}
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script>
|
|
||||||
converse.initialize({'bosh_service_url': 'https://conversejs.org/http-bind/'});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,24 +2,20 @@
|
|||||||
<h4 class="sidebar-title">Sponsored by</h4>
|
<h4 class="sidebar-title">Sponsored by</h4>
|
||||||
</span>
|
</span>
|
||||||
<ul class="sponsors-list">
|
<ul class="sponsors-list">
|
||||||
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img style="height: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
|
<img style="width: 10em" src="/media/logos/bairesdev-primary.png" alt="BairesDev">
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li><a href="https://www.codefirst.co.uk/?utm_source=conversejs" target="_blank" rel="noopener">
|
|
||||||
<img style="width: 9em" src="/logo/codefirst.png" alt="Codefirst">
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="https://blokt.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
<li><a href="https://blokt.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img style="width: 9em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy">
|
<img style="width: 9em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="https://www.pluckeye.net/?utm_source=conversejs" target="_blank" rel="noopener">
|
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img style="width: 8em" src="/logo/pluckeye.svg" alt="Pluckeye">
|
<img style="width: 9em" src="/media/logos/primesound.png" alt="Prime Sound">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener">
|
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img style="width: 9em" src="/logo/originalenergie-black.png" alt="Original Energie">
|
<img style="height: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/api/index.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
============================================
|
============================================
|
||||||
The API documentation (generated with JSDoc)
|
The API documentation (generated with JSDoc)
|
||||||
|
@ -48,9 +48,9 @@ copyright = u'2018, JC Brand'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '8.0.1'
|
version = '10.1.6'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '8.0.1'
|
release = '10.1.6'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -23,6 +23,38 @@ all the available configuration settings.
|
|||||||
Configuration settings
|
Configuration settings
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
.. _`allowed_audio_domains`:
|
||||||
|
|
||||||
|
allowed_audio_domains
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Default: ``null``
|
||||||
|
|
||||||
|
If falsy, all domains are allowed. Set it to an array to specify a whitelist of allowed domains.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`allowed_image_domains`:
|
||||||
|
|
||||||
|
allowed_image_domains
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Default: ``null``
|
||||||
|
|
||||||
|
If falsy, all domains are allowed. Set it to an array to specify a whitelist of allowed domains.
|
||||||
|
|
||||||
|
E.g. ``['imgur.com', 'imgbb.com']``
|
||||||
|
|
||||||
|
.. _`allowed_video_domains`:
|
||||||
|
|
||||||
|
allowed_video_domains
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Default: ``null``
|
||||||
|
|
||||||
|
If falsy, all domains are allowed. Set it to an array to specify a whitelist of allowed domains.
|
||||||
|
|
||||||
|
E.g. ``['imgur.com']``
|
||||||
|
|
||||||
authentication
|
authentication
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@ -118,13 +150,6 @@ This setting is only applicable if the ``converse-bookmarks`` plugin is loaded.
|
|||||||
|
|
||||||
See also: `allow_public_bookmarks`_
|
See also: `allow_public_bookmarks`_
|
||||||
|
|
||||||
allow_chat_pending_contacts
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
* Default: ``false``
|
|
||||||
|
|
||||||
Allow the user to chat with pending contacts.
|
|
||||||
|
|
||||||
allow_contact_removal
|
allow_contact_removal
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@ -149,7 +174,7 @@ allow_dragresize
|
|||||||
* Default: ``true``
|
* Default: ``true``
|
||||||
|
|
||||||
Allow users to resize chats by dragging the edges. The min-height and min-width
|
Allow users to resize chats by dragging the edges. The min-height and min-width
|
||||||
CSS properties set on a chatboxes (specifically on the ``#converse.js .chatbox > .box-flyout`` element)
|
CSS properties set on a chatboxes (specifically on the ``.chatbox > .box-flyout`` element)
|
||||||
will be honored, IF they are set in pixels.
|
will be honored, IF they are set in pixels.
|
||||||
|
|
||||||
allow_logout
|
allow_logout
|
||||||
@ -192,13 +217,6 @@ allow_message_styling
|
|||||||
|
|
||||||
Determines wehether support for XEP-0393 Message Styling hints are enabled or not.
|
Determines wehether support for XEP-0393 Message Styling hints are enabled or not.
|
||||||
|
|
||||||
allow_muc
|
|
||||||
---------
|
|
||||||
|
|
||||||
* Default: ``true``
|
|
||||||
|
|
||||||
Allow multi-user chat (muc) in chatrooms. Setting this to ``false`` will remove
|
|
||||||
the ``Chatrooms`` tab from the control box.
|
|
||||||
|
|
||||||
allow_muc_invitations
|
allow_muc_invitations
|
||||||
---------------------
|
---------------------
|
||||||
@ -389,7 +407,6 @@ in to their XMPP account.
|
|||||||
So currently if EITHER ``keepalive`` or ``auto_login`` is ``true`` and
|
So currently if EITHER ``keepalive`` or ``auto_login`` is ``true`` and
|
||||||
`authentication`_ is set to ``login``, then Converse will try to log the user in.
|
`authentication`_ is set to ``login``, then Converse will try to log the user in.
|
||||||
|
|
||||||
|
|
||||||
auto_away
|
auto_away
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -676,7 +693,7 @@ appear in another.
|
|||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
converse.initialize({
|
converse.initialize({
|
||||||
connection_options: { 'worker': true }
|
connection_options: { worker: '/dist/shared-connection-worker.js' }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -735,6 +752,14 @@ loglevel
|
|||||||
|
|
||||||
You can also set this value by changing a URL fragment `#converse?loglevel=debug`
|
You can also set this value by changing a URL fragment `#converse?loglevel=debug`
|
||||||
|
|
||||||
|
.. _`dark_theme`:
|
||||||
|
|
||||||
|
dark_theme
|
||||||
|
----------
|
||||||
|
|
||||||
|
* Default: ``'dracula'``
|
||||||
|
|
||||||
|
The theme being used in dark mode.
|
||||||
|
|
||||||
default_domain
|
default_domain
|
||||||
--------------
|
--------------
|
||||||
@ -751,14 +776,6 @@ JIDs with other domains are still allowed but need to be provided in full.
|
|||||||
To specify only one domain and disallow other domains, see the `locked_domain`_
|
To specify only one domain and disallow other domains, see the `locked_domain`_
|
||||||
option.
|
option.
|
||||||
|
|
||||||
registration_domain
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
* Default: ``''``
|
|
||||||
|
|
||||||
Specify a domain name for which the registration form will be fetched automatically,
|
|
||||||
without the user having to enter any XMPP server domain name.
|
|
||||||
|
|
||||||
default_state
|
default_state
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@ -793,28 +810,6 @@ domain_placeholder
|
|||||||
The placeholder text shown in the domain input on the registration form.
|
The placeholder text shown in the domain input on the registration form.
|
||||||
|
|
||||||
|
|
||||||
embed_audio
|
|
||||||
-----------
|
|
||||||
|
|
||||||
* Default: ``true``
|
|
||||||
|
|
||||||
If set to ``false``, audio files won't be embedded in chats, instead only their links will be shown.
|
|
||||||
|
|
||||||
It also accepts an array strings of whitelisted domain names to only render audio files that belong to those domains.
|
|
||||||
E.g. ``['conversejs.org']``
|
|
||||||
|
|
||||||
|
|
||||||
embed_videos
|
|
||||||
------------
|
|
||||||
|
|
||||||
* Default: ``true``
|
|
||||||
|
|
||||||
If set to ``false``, videos won't be rendered in chats, instead only their links will be shown.
|
|
||||||
|
|
||||||
It also accepts an array strings of whitelisted domain names to only render videos that belong to those domains.
|
|
||||||
E.g. ``['imgur.com', 'imgbb.com']``
|
|
||||||
|
|
||||||
|
|
||||||
emoji_categories
|
emoji_categories
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@ -906,7 +901,7 @@ The app servers are specified with the `push_app_servers`_ option.
|
|||||||
enable_smacks
|
enable_smacks
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
* Default: ``false``
|
* Default: ``true``
|
||||||
|
|
||||||
Determines whether `XEP-0198 Stream Management <https://xmpp.org/extensions/xep-0198.html>`_
|
Determines whether `XEP-0198 Stream Management <https://xmpp.org/extensions/xep-0198.html>`_
|
||||||
support is turned on or not.
|
support is turned on or not.
|
||||||
@ -930,12 +925,6 @@ filter_url_query_params
|
|||||||
|
|
||||||
Accepts a string or array of strings. Any query strings from URLs that match this setting will be removed.
|
Accepts a string or array of strings. Any query strings from URLs that match this setting will be removed.
|
||||||
|
|
||||||
fullname
|
|
||||||
--------
|
|
||||||
|
|
||||||
If you are using prebinding, can specify the fullname of the currently
|
|
||||||
logged in user, otherwise the user's vCard will be fetched.
|
|
||||||
|
|
||||||
geouri_regex
|
geouri_regex
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -966,22 +955,6 @@ hide_offline_users
|
|||||||
|
|
||||||
If set to ``true``, then offline users aren't shown in the roster.
|
If set to ``true``, then offline users aren't shown in the roster.
|
||||||
|
|
||||||
hide_open_bookmarks
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
* Default: ``false`` (``true`` when the `view_mode`_ is set to ``fullscreen``).
|
|
||||||
|
|
||||||
This setting applies to the ``converse-bookmarks`` plugin and specfically the
|
|
||||||
list of bookmarks shown in the ``Rooms`` tab of the control box.
|
|
||||||
|
|
||||||
By default all bookmarks are shown in that list, if this setting is set to
|
|
||||||
``true``, then only bookmarks for rooms not currently open (i.e. that the
|
|
||||||
current user hasn't joined), are shown.
|
|
||||||
|
|
||||||
Makes sense to set this to ``true`` when also using the non-core
|
|
||||||
``converse-roomslist`` plugin, which shows a list of currently open (i.e.
|
|
||||||
"joined") rooms.
|
|
||||||
|
|
||||||
.. _`i18n`:
|
.. _`i18n`:
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
@ -1206,22 +1179,6 @@ from the XMPP server.
|
|||||||
|
|
||||||
Used in conjunction with ``message_archiving`` and in context of `XEP-0313: Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_.
|
Used in conjunction with ``message_archiving`` and in context of `XEP-0313: Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_.
|
||||||
|
|
||||||
message_carbons
|
|
||||||
---------------
|
|
||||||
|
|
||||||
* Default: ``true``
|
|
||||||
|
|
||||||
Support for `XEP-0280: Message Carbons <https://xmpp.org/extensions/xep-0280.html>`_
|
|
||||||
|
|
||||||
In order to keep all IM clients for a user engaged in a conversation,
|
|
||||||
outbound messages are carbon-copied to all interested resources.
|
|
||||||
|
|
||||||
This is especially important with Converse, where each browser
|
|
||||||
tab serves as a separate IM client.
|
|
||||||
|
|
||||||
XEP-0280 requires server support, so make sure that message carbons are enabled
|
|
||||||
on your server.
|
|
||||||
|
|
||||||
|
|
||||||
message_limit
|
message_limit
|
||||||
-------------
|
-------------
|
||||||
@ -1449,11 +1406,10 @@ Example:
|
|||||||
muc_show_info_messages
|
muc_show_info_messages
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
* Default: List composed of MUC status codes, role changes, join and leave events
|
* Default: List composed of MUC status codes, role changes, join and leave events and affiliation changes. The values of converse.MUC_INFO_CODES below are joined to build the default list:
|
||||||
and affiliation changes. The values of converse.MUC_INFO_CODES below are joined to
|
|
||||||
build the default list:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
converse.MUC_AFFILIATION_CHANGES_LIST = ['owner', 'admin', 'member', 'exowner', 'exadmin', 'exmember', 'exoutcast']
|
converse.MUC_AFFILIATION_CHANGES_LIST = ['owner', 'admin', 'member', 'exowner', 'exadmin', 'exmember', 'exoutcast']
|
||||||
converse.MUC_ROLE_CHANGES_LIST = ['op', 'deop', 'voice', 'mute'];
|
converse.MUC_ROLE_CHANGES_LIST = ['op', 'deop', 'voice', 'mute'];
|
||||||
converse.MUC_TRAFFIC_STATES_LIST = ['entered', 'exited'];
|
converse.MUC_TRAFFIC_STATES_LIST = ['entered', 'exited'];
|
||||||
@ -1475,6 +1431,7 @@ It is recommended to use the aforementioned Converse object in the following fas
|
|||||||
to build the list of desired info messages that will be shown:
|
to build the list of desired info messages that will be shown:
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
muc_show_info_messages: [
|
muc_show_info_messages: [
|
||||||
...converse.MUC_INFO_CODES.visibility_changes,
|
...converse.MUC_INFO_CODES.visibility_changes,
|
||||||
...converse.MUC_INFO_CODES.self,
|
...converse.MUC_INFO_CODES.self,
|
||||||
@ -1504,19 +1461,6 @@ a nickname configured for it), you'll see the message history (if the
|
|||||||
server supports `XEP-0313 Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_)
|
server supports `XEP-0313 Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_)
|
||||||
and the nickname form at the bottom.
|
and the nickname form at the bottom.
|
||||||
|
|
||||||
muc_show_ogp_unfurls
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
* Default: ``true``
|
|
||||||
|
|
||||||
Supports showing extra metadata (picture and description) for URLs contained in
|
|
||||||
groupchat messages.
|
|
||||||
|
|
||||||
The metadata must come from the MUC itself, metadata sent from participants
|
|
||||||
themselves will not be shown.
|
|
||||||
|
|
||||||
For Prosody XMPP server, `mod_ogp <https://modules.prosody.im/mod_ogp.html>`_ can be used.
|
|
||||||
|
|
||||||
|
|
||||||
muc_subscribe_to_rai
|
muc_subscribe_to_rai
|
||||||
--------------------
|
--------------------
|
||||||
@ -1723,11 +1667,12 @@ compile time.
|
|||||||
|
|
||||||
This configuration seting allows this value to be set at runtime as well.
|
This configuration seting allows this value to be set at runtime as well.
|
||||||
|
|
||||||
|
.. _persistent-store:
|
||||||
|
|
||||||
persistent_store
|
persistent_store
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* Default: ``localStorage``
|
* Default: ``IndexedDB``
|
||||||
* Valid options: ``localStorage``, ``IndexedDB``, ``sessionStorage``, ``BrowserExtLocal``, ``BrowserExtSync``
|
* Valid options: ``localStorage``, ``IndexedDB``, ``sessionStorage``, ``BrowserExtLocal``, ``BrowserExtSync``
|
||||||
|
|
||||||
Determines which store is used for storing persistent data.
|
Determines which store is used for storing persistent data.
|
||||||
@ -1813,6 +1758,82 @@ For example:
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
registration_domain
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* Default: ``''``
|
||||||
|
|
||||||
|
Specify a domain name for which the registration form will be fetched automatically,
|
||||||
|
without the user having to enter any XMPP server domain name.
|
||||||
|
|
||||||
|
render_media
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Default: ``true``
|
||||||
|
|
||||||
|
* Possible values: ``true``, ``false`` or an array of domains for which media
|
||||||
|
should automatically be rendered.
|
||||||
|
|
||||||
|
If ``true``, media URLs (images, audio and video) will be rendered in the chat.
|
||||||
|
|
||||||
|
If ``false``, the URLs won't render as media, and instead only clickable links
|
||||||
|
will be shown.
|
||||||
|
|
||||||
|
Setting it to an array of domains means that media will be rendered only for URLs
|
||||||
|
matching those domains.
|
||||||
|
|
||||||
|
Media URLs are those URLs which appear to point to media files as well as URLs
|
||||||
|
for which `Open Graph Protocol (OGP) <https://modules.prosody.im/mod_ogp.html>`_
|
||||||
|
data was received (for example via Prosody's `mod_ogp <https://modules.prosody.im/mod_ogp.html>`_).
|
||||||
|
|
||||||
|
The OGP metadata must come from the MUC itself, metadata sent from participants
|
||||||
|
themselves will not be shown.
|
||||||
|
|
||||||
|
Based on the OGP metadata Converse will render a URL preview (also known as an
|
||||||
|
"unfurl").
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Note, even if this setting is ``false`` (or if the URL domain is not in the
|
||||||
|
array of allowed domains), a user can still click on the message
|
||||||
|
dropdown and click to show or hide the media for that particular message.
|
||||||
|
|
||||||
|
If you want to disable this ability, you can set the allowed domains for the
|
||||||
|
media type to an empty array.
|
||||||
|
|
||||||
|
See:
|
||||||
|
|
||||||
|
* `allowed_audio_domains`_
|
||||||
|
* `allowed_video_domains`_
|
||||||
|
* `allowed_image_domains`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This setting, together with the three allowed domain settings above, obsolete
|
||||||
|
the ``show_images_inline``, ``embed_audio`` and ``embed_videos`` settings.
|
||||||
|
|
||||||
|
|
||||||
|
reuse_scram_keys
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* Default: ``false``
|
||||||
|
|
||||||
|
Most XMPP servers enable the Salted Challenge Response Authentication Mechanism
|
||||||
|
or SCRAM for short. This allows the user and the server to mutually
|
||||||
|
authenticate *without* the need to transmit the user's password in plaintext.
|
||||||
|
|
||||||
|
Assuming the server does not alter the user's password or the
|
||||||
|
storage parameters, we can authenticate with the same SCRAM key multiple times.
|
||||||
|
|
||||||
|
This opens an opportunity: we can store the user's login credentials in the
|
||||||
|
browser without storing the sensitive plaintext password, or the
|
||||||
|
need to set up complicated third party backends, like OAuth.
|
||||||
|
|
||||||
|
Enabling this option will let Converse save a user's SCRAM keys upon successful
|
||||||
|
login, and next time Converse is loaded the user will be automatically logged in
|
||||||
|
with those SCRAM keys.
|
||||||
|
|
||||||
|
|
||||||
.. _`roomconfig_whitelist`:
|
.. _`roomconfig_whitelist`:
|
||||||
|
|
||||||
roomconfig_whitelist
|
roomconfig_whitelist
|
||||||
@ -1919,6 +1940,16 @@ show_client_info
|
|||||||
Specifies whether the info icon is shown on the controlbox which when clicked opens an
|
Specifies whether the info icon is shown on the controlbox which when clicked opens an
|
||||||
"About" modal with more information about the version of Converse being used.
|
"About" modal with more information about the version of Converse being used.
|
||||||
|
|
||||||
|
show_connection_url_input
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
* Default: ``false``
|
||||||
|
|
||||||
|
Determines whether the login form should show an input element where the user
|
||||||
|
can enter the connection URL. If it's a websocket url, then upon form
|
||||||
|
submission the `websocket_url`_ setting will be updated with this value, and if
|
||||||
|
it's an HTTP URL then the `bosh_service_url`_ setting will be updated.
|
||||||
|
|
||||||
show_controlbox_by_default
|
show_controlbox_by_default
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
@ -1973,8 +2004,8 @@ show_images_inline
|
|||||||
|
|
||||||
If set to ``false``, images won't be rendered in chats, instead only their links will be shown.
|
If set to ``false``, images won't be rendered in chats, instead only their links will be shown.
|
||||||
|
|
||||||
It also accepts an array strings of whitelisted domain names to only render images that belong to those domains.
|
Users will however still have the ability to render individual images via the message actions dropdown.
|
||||||
E.g. ``['imgur.com', 'imgbb.com']``
|
If you want to disallow users from doing so, set the ``allowed_image_domains`` option to an empty array ``[]``.
|
||||||
|
|
||||||
|
|
||||||
show_retraction_warning
|
show_retraction_warning
|
||||||
@ -2052,6 +2083,13 @@ themselves).
|
|||||||
In order to support all browsers we need both an MP3 and an Ogg file. Make sure
|
In order to support all browsers we need both an MP3 and an Ogg file. Make sure
|
||||||
to name your files ``msg_received.ogg`` and ``msg_received.mp3``.
|
to name your files ``msg_received.ogg`` and ``msg_received.mp3``.
|
||||||
|
|
||||||
|
stanza_timeout
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* Default: ``20000`` (20 seconds)
|
||||||
|
|
||||||
|
The time to wait, in milliseconds, for a response stanza (for example to an IQ
|
||||||
|
request), before a timeout error is thrown and Converse stops waiting.
|
||||||
|
|
||||||
sticky_controlbox
|
sticky_controlbox
|
||||||
-----------------
|
-----------------
|
||||||
@ -2109,11 +2147,13 @@ If set to ``false``, this feature is disabled.
|
|||||||
|
|
||||||
If set to ``a resource name``, Converse will synchronize only with a client that has that particular resource assigned to it.
|
If set to ``a resource name``, Converse will synchronize only with a client that has that particular resource assigned to it.
|
||||||
|
|
||||||
|
.. _`theme`:
|
||||||
|
|
||||||
theme
|
theme
|
||||||
-----
|
-----
|
||||||
|
|
||||||
* Default: ``default``
|
* Default: ``default``
|
||||||
* Valid options: ``default``, ``concord``
|
* Valid options: ``default``, ``concord``, ``dracula``
|
||||||
|
|
||||||
Let's you set a color theme for Converse.
|
Let's you set a color theme for Converse.
|
||||||
|
|
||||||
@ -2214,7 +2254,7 @@ The ``view_mode`` setting configures Converse's mode and resulting behavior.
|
|||||||
|
|
||||||
* ``overlayed`` is the original mode, in which the chats appeared as small boxes overlaying a webpage which can contain arbitrary content.
|
* ``overlayed`` is the original mode, in which the chats appeared as small boxes overlaying a webpage which can contain arbitrary content.
|
||||||
* ``fullscreen`` is for a fullpage and single-page app.
|
* ``fullscreen`` is for a fullpage and single-page app.
|
||||||
* ``embedded`` is used to embed a single chat into a DOM element in the page. The DOM element must have the id ``#conversejs``.
|
* ``embedded`` is used to embed Converse into a particular part of the web page. Put the custom element ``<converse-root></converse-root>`` into your page HTML there were you want Converse to appear. See `conversejs.org/demo/embedded.html <https://conversejs.org/demo/embedded.html>`_ for an example of this view mode.
|
||||||
* ``mobile`` is for smaller viewports. Converse is responsive, and the other views will also resize to fit a smaller viewport, but certain behavioural changes won't be made. For example, when using ``overlayed`` in a mobile view, Converse won't know which chat is currently visible and therefore won't be able to properly show notifications for chats that are obscured.
|
* ``mobile`` is for smaller viewports. Converse is responsive, and the other views will also resize to fit a smaller viewport, but certain behavioural changes won't be made. For example, when using ``overlayed`` in a mobile view, Converse won't know which chat is currently visible and therefore won't be able to properly show notifications for chats that are obscured.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -1,48 +1,57 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/dependencies.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
=============================
|
.. _`development`:
|
||||||
Starting up a dev environment
|
|
||||||
=============================
|
============================
|
||||||
|
Setting up a dev environment
|
||||||
|
============================
|
||||||
|
|
||||||
Installing the 3rd party dependencies
|
Installing the 3rd party dependencies
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
We use development tools which depend on Node.js and npm (the Node package manager).
|
To develop and customize Converse, you'll first need to check out Converse's Git
|
||||||
|
repository:
|
||||||
If you don't have Node.js installed, you can download and install the latest
|
|
||||||
version `here <https://nodejs.org/download>`_.
|
|
||||||
|
|
||||||
Alternatively you can `use your operating system's package manager to install
|
|
||||||
Node.js <https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions>`_.
|
|
||||||
|
|
||||||
Also make sure you have ``Git`` installed. `See here <http://git-scm.com/book/en/Getting-Started-Installing-Git>`_.
|
|
||||||
|
|
||||||
Now use ``git`` to check out the Converse repository:
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
git clone https://github.com/conversejs/converse.js.git
|
git clone https://github.com/conversejs/converse.js.git
|
||||||
|
cd converse.js
|
||||||
|
|
||||||
Now go into the repository checkout and run ``make dev`` in order to set up the
|
|
||||||
development environment.
|
We use development tools which depend on Node.js and NPM (the Node package manager).
|
||||||
|
|
||||||
|
It's recommended that you use `NVM <https://github.com/nvm-sh/nvm>`_ (the Node version manager)
|
||||||
|
to make sure you have the right version of Node.
|
||||||
|
|
||||||
|
Refer to the `NVM Github page <https://github.com/nvm-sh/nvm#install--update-script>`_ for instructions on how to install it.
|
||||||
|
|
||||||
|
Once NVM is installed, you can run the following inside your checkout of the Converse Git repository:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
nvm install
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
You will always have to first run ``nvm install`` in a new terminal session before working on Converse.
|
||||||
|
|
||||||
|
|
||||||
|
To set up the Converse development environment, you now run ``make dev``.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd converse.js
|
|
||||||
make dev
|
make dev
|
||||||
|
|
||||||
If you're using Windows, or don't have GNU Make installed, you can run the
|
Alternatively, if you're using Windows, or don't have GNU Make installed, you can run the
|
||||||
following:
|
following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd converse.js
|
|
||||||
npm install
|
npm install
|
||||||
npm run lerna
|
npm run lerna
|
||||||
|
|
||||||
This will install the Node.js development tools and Converse's dependencies.
|
This will install the Node development tools and Converse's dependencies.
|
||||||
|
|
||||||
The front-end dependencies are those JavaScript files on which
|
The front-end dependencies are those JavaScript files on which
|
||||||
Converse directly depends and which will be loaded in the browser as part of
|
Converse directly depends and which will be loaded in the browser as part of
|
||||||
@ -80,3 +89,51 @@ under the `GPLv3 <https://github.com/signalapp/libsignal-protocol-javascript/blo
|
|||||||
which requires all other dependent JavaScript code to also be open sourced under the same
|
which requires all other dependent JavaScript code to also be open sourced under the same
|
||||||
license. You might not be willing to adhere to those terms, which is why you
|
license. You might not be willing to adhere to those terms, which is why you
|
||||||
need to decide for yourself whether you're going to load libsignal or not.
|
need to decide for yourself whether you're going to load libsignal or not.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`webserver`:
|
||||||
|
|
||||||
|
Setting up a webserver
|
||||||
|
======================
|
||||||
|
|
||||||
|
When making changes to Converse, either development or theming changes,
|
||||||
|
you'll want to preview them in your browser.
|
||||||
|
|
||||||
|
For this, you'll need to serve the development files via a web server,
|
||||||
|
so that you can see your local changes in the browser.
|
||||||
|
|
||||||
|
Manually starting a web server
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
To both set up the development environment and also start up a web browser to
|
||||||
|
serve the files for you, you can run::
|
||||||
|
|
||||||
|
make serve
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
||||||
|
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
||||||
|
available via your package manager. For Mac, you'll need to install XCode and in
|
||||||
|
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
||||||
|
|
||||||
|
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
||||||
|
|
||||||
|
When developing or changing the theme, you'll want to load all the
|
||||||
|
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
||||||
|
|
||||||
|
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
||||||
|
how ``converse.initialize`` is called and to potentially change any of the
|
||||||
|
settings.
|
||||||
|
|
||||||
|
Starting a web server with live reloading
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
||||||
|
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
||||||
|
|
||||||
|
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
||||||
|
is now being used as the HTML template, and you'll need to modify that file if
|
||||||
|
you want to change the settings passed to ``converse.initialize``.
|
||||||
|
|
||||||
|
If you're running ``make devserver``, you need to open http://localhost:8080.
|
||||||
|
|
||||||
|
@ -23,11 +23,10 @@ to fix a bug or to add new functionality.
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
dependencies
|
setup_dev_environment
|
||||||
style_guide
|
|
||||||
webserver
|
|
||||||
plugin_development
|
plugin_development
|
||||||
api/index
|
api/index
|
||||||
testing
|
testing
|
||||||
other_frameworks
|
other_frameworks
|
||||||
builds
|
builds
|
||||||
|
style_guide
|
||||||
|
@ -31,10 +31,16 @@ End to end message encryption (`XEP-0384 OMEMO <https://xmpp.org/extensions/xep-
|
|||||||
=============================================================================================
|
=============================================================================================
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Converse.js (as of version 4.1.2) does NOT support encryption or decryption
|
Converse versions older than 8.0.0 do NOT support encryption or decryption
|
||||||
of uploaded files. Files will be uploaded WITHOUT ENCRYPTION, even when
|
of uploaded files. Files will be uploaded WITHOUT ENCRYPTION, even when
|
||||||
OMEMO is enabled.
|
OMEMO is enabled.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
For end-to-end encryption via OMEMO, you'll need to load `libsignal-protocol.js
|
||||||
|
<https://github.com/signalapp/libsignal-protocol-javascript>`_ separately in
|
||||||
|
your page. Take a look at the section on :ref:`libsignal <dependency-libsignal>` and the
|
||||||
|
:ref:`security considerations around OMEMO <feature-omemo>`.
|
||||||
|
|
||||||
Converse supports OMEMO encryption based on the
|
Converse supports OMEMO encryption based on the
|
||||||
`Signal Protocol <https://github.com/signalapp/libsignal-protocol-javascript>`_.
|
`Signal Protocol <https://github.com/signalapp/libsignal-protocol-javascript>`_.
|
||||||
|
|
||||||
@ -44,7 +50,9 @@ ever tear down a session once one has been established.
|
|||||||
|
|
||||||
This means that a session needs to be stored permanently after logging out.
|
This means that a session needs to be stored permanently after logging out.
|
||||||
|
|
||||||
Converse stores this session information in the browser's `localStorage <https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage>`_.
|
Converse stores this session information in the browser's `IndexedDB <https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API>`_
|
||||||
|
or `localStorage <https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage>`_
|
||||||
|
database, depending on the value provided to :ref:`persistent-store`.
|
||||||
|
|
||||||
If you've checked the "This is not a trusted device" checkbox when logging in,
|
If you've checked the "This is not a trusted device" checkbox when logging in,
|
||||||
then `sessionStorage <https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage>`_
|
then `sessionStorage <https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage>`_
|
||||||
@ -88,10 +96,7 @@ headers.
|
|||||||
Due to these reasons, it's NOT a good idea to use encrypted messaging with a
|
Due to these reasons, it's NOT a good idea to use encrypted messaging with a
|
||||||
browser-based solution in life-threatening situations.
|
browser-based solution in life-threatening situations.
|
||||||
|
|
||||||
Security can be increased by using an installable app (like one based on `Electron <https://electronjs.org/>`_)
|
Security can be increased by using an installable app (like `Converse Desktop <https://github.com/conversejs/converse-desktop>`_).
|
||||||
with a strict Content Security Policy.
|
|
||||||
|
|
||||||
Look out for an Electron based version of Converse coming in the following months.
|
|
||||||
|
|
||||||
For further reading on the challenges of web-based crypto, take a look at these
|
For further reading on the challenges of web-based crypto, take a look at these
|
||||||
articles:
|
articles:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/other_frameworks.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
Integrating converse.js into other frameworks
|
Integrating converse.js into other frameworks
|
||||||
=============================================
|
=============================================
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/plugin_development.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
.. _`writing-a-plugin`:
|
.. _`writing-a-plugin`:
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ and is itself composed out of plugins.
|
|||||||
There are only a few files that are included in the default build of Converse
|
There are only a few files that are included in the default build of Converse
|
||||||
which aren't plugins.
|
which aren't plugins.
|
||||||
|
|
||||||
An important one is `converse-core.js <https://github.com/conversejs/converse.js/blob/master/src/headless/converse-core.js>`_,
|
An important one is `core.js <https://github.com/conversejs/.js/blob/master/src/headless/core.js>`_,
|
||||||
which is responsible for bootstrapping the plugin architecture,
|
which is responsible for bootstrapping the plugin architecture,
|
||||||
setting up and maintaining the connection to the XMPP
|
setting up and maintaining the connection to the XMPP
|
||||||
server and declaring the public (`window.converse </docs/html/api/converse.html>`_) and protected (`_converse.api </docs/html/api/-_converse.api.html>`_) APIs.
|
server and declaring the public (`window.converse </docs/html/api/converse.html>`_) and protected (`_converse.api </docs/html/api/-_converse.api.html>`_) APIs.
|
||||||
@ -180,8 +180,56 @@ The code for it could look something like this:
|
|||||||
These dependencies are closured so that they don't pollute the global
|
These dependencies are closured so that they don't pollute the global
|
||||||
namespace, that's why you need to access them in such a way inside the module.
|
namespace, that's why you need to access them in such a way inside the module.
|
||||||
|
|
||||||
Overrides
|
Overriding templates
|
||||||
---------
|
--------------------
|
||||||
|
|
||||||
|
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
|
||||||
|
templates and templates are imported as separate files.
|
||||||
|
|
||||||
|
It's possible to configure your module bundler (e.g. Webpack) in such as way that a
|
||||||
|
different file is loaded when a template is imported.
|
||||||
|
|
||||||
|
This allows you to create your own templates that are used instead of the ones
|
||||||
|
that would have originally been imported.
|
||||||
|
|
||||||
|
With Webpack (which Converse uses internally), you can specify an
|
||||||
|
``alias`` for the template you want to override. This alias then points to your
|
||||||
|
own custom template.
|
||||||
|
|
||||||
|
For example, in your webpack config file, you could add the following to the
|
||||||
|
``config`` object that gets exported:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js'],
|
||||||
|
modules: [
|
||||||
|
path.join(__dirname, 'node_modules'),
|
||||||
|
path.join(__dirname, 'node_modules/converse.js/src')
|
||||||
|
],
|
||||||
|
alias: {
|
||||||
|
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
|
||||||
|
with your own template at the path ``templates/custom-profile.js`` (relative to your webpack config file).
|
||||||
|
|
||||||
|
|
||||||
|
Object and class Overrides
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. note:: Using the `overrides` feature from pluggable.js is discouraged. It's
|
||||||
|
much better to use events, promises and `hooks`_ to modify the behaviour of
|
||||||
|
Converse.
|
||||||
|
|
||||||
|
The pluggable.js `overrides` will only work on objects and classes that are
|
||||||
|
set as attributes on the `_converse` object, which doesn't apply to many
|
||||||
|
newer classes and objects, such as the web components. For these clasess,
|
||||||
|
overrides won't work at all.
|
||||||
|
|
||||||
|
This section is left here for completeness, because in some special cases
|
||||||
|
overrides are still used.
|
||||||
|
|
||||||
Plugins can override core code or code from other plugins. You can specify
|
Plugins can override core code or code from other plugins. You can specify
|
||||||
overrides in the object passed to ``converse.plugins.add``.
|
overrides in the object passed to ``converse.plugins.add``.
|
||||||
@ -242,7 +290,7 @@ monkey patching which pollutes the call stack and can make your code fragile
|
|||||||
and prone to bugs when Converse gets updated. Too much use of ``overrides``
|
and prone to bugs when Converse gets updated. Too much use of ``overrides``
|
||||||
is therefore a "code smell" which should ideally be avoided.
|
is therefore a "code smell" which should ideally be avoided.
|
||||||
|
|
||||||
A better approach is to listen to the events emitted by Converse, and to add
|
A better approach is to use the events and `hooks`_ emitted by Converse, and to add
|
||||||
your code in event handlers. This is however not always possible, in which case
|
your code in event handlers. This is however not always possible, in which case
|
||||||
the overrides are a powerful tool.
|
the overrides are a powerful tool.
|
||||||
|
|
||||||
@ -260,40 +308,6 @@ For example:
|
|||||||
Object.assign(_converse.ChatBoxView.prototype, { doSomething });
|
Object.assign(_converse.ChatBoxView.prototype, { doSomething });
|
||||||
|
|
||||||
|
|
||||||
Overriding a template
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
|
|
||||||
templates.
|
|
||||||
|
|
||||||
It's not possible to override a template with the plugin's ``overrides``
|
|
||||||
feature, instead you should configure a new path to your own template via your
|
|
||||||
module bundler.
|
|
||||||
|
|
||||||
For example, with Webpack (which Converse uses internall), you can specify an
|
|
||||||
``alias`` for the template you want to override. This alias then points to your
|
|
||||||
own custom template.
|
|
||||||
|
|
||||||
For example, in your webpack config file, you could add the following to the
|
|
||||||
``config`` object that gets exported:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js'],
|
|
||||||
modules: [
|
|
||||||
path.join(__dirname, 'node_modules'),
|
|
||||||
path.join(__dirname, 'node_modules/converse.js/src')
|
|
||||||
],
|
|
||||||
alias: {
|
|
||||||
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
|
|
||||||
with your own template at the path ``templates/custom-profile.js`` (relative to
|
|
||||||
your webpack config file).
|
|
||||||
|
|
||||||
|
|
||||||
.. _`dependencies`:
|
.. _`dependencies`:
|
||||||
|
|
||||||
@ -436,8 +450,87 @@ Please refer to the `API documentation </docs/html/api/http://localhost:8008/doc
|
|||||||
for an overview of what's available to you. If you need new events or promises, then
|
for an overview of what's available to you. If you need new events or promises, then
|
||||||
`please open an issue or make a pull request on Github <https://github.com/jcbrand/converse.js>`_
|
`please open an issue or make a pull request on Github <https://github.com/jcbrand/converse.js>`_
|
||||||
|
|
||||||
A full example plugin
|
|
||||||
---------------------
|
Hooks
|
||||||
|
-----
|
||||||
|
|
||||||
|
Converse has the concept of ``hooks``, which are special events that allow you
|
||||||
|
to modify it's behaviour at runtime.
|
||||||
|
|
||||||
|
A hook is similar to an event, but it differs in two meaningful ways:
|
||||||
|
|
||||||
|
1. Converse will wait for all handlers of a hook to finish before continuing inside the function from where the hook was triggered.
|
||||||
|
2. Each hook contains a payload, which the handlers can modify or extend, before returning it (either to the function that triggered the hook or to subsequent handlers).
|
||||||
|
|
||||||
|
These two properties of hooks makes it possible for 3rd party plugins to
|
||||||
|
intercept and update data, allowing them to modify Converse without the need of
|
||||||
|
resorting to `overrides`_.
|
||||||
|
|
||||||
|
A hook is triggered in the following way:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
async function hookTriggerExample () {
|
||||||
|
const payload = { foo: 'bar' };
|
||||||
|
const updated_payload = await api.hook('hookName', this, payload);
|
||||||
|
return updated_payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
The above could be shortened:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
async function hookTriggerExample () {
|
||||||
|
return await api.hook('hookName', this, { foo: 'bar' });
|
||||||
|
}
|
||||||
|
|
||||||
|
The ``async/await`` syntax could also be removed, but then it's less clear to
|
||||||
|
the reader that this function returns a promise.
|
||||||
|
|
||||||
|
Let's assume that in a plugin somewhere a listener is registered for this hook:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
function hookListenerExample () {
|
||||||
|
|
||||||
|
api.listen.on('hookname', (context, payload) => {
|
||||||
|
return {...payload, 'baz': 'buzz'};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
The ``context`` parameter in our listener is usually the ``this`` of the function
|
||||||
|
that triggered the hook (as is the case in ``hookTriggerExample``),
|
||||||
|
but could also be a ``Model`` instance.
|
||||||
|
|
||||||
|
The ``payload`` parameter is the same payload that was passed in in
|
||||||
|
``hookTriggerExample``.
|
||||||
|
|
||||||
|
The ``hookListenerExample`` function accepts the payload and returns a new one
|
||||||
|
which contains the original payload together with a new key and value.
|
||||||
|
|
||||||
|
The ``updated_payload`` that is now returned from ``hookTriggerExample`` looks
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{ foo: 'bar', bazz: 'buzz' }
|
||||||
|
|
||||||
|
Our plugin was able to add data to the payload without requiring any kind of
|
||||||
|
knowledge from ``hookTriggerExample`` about ``hookListenerExample`` and
|
||||||
|
without any kind of coupling betwee the code.
|
||||||
|
|
||||||
|
A good example of a real-world hook in Converse, is the
|
||||||
|
`getMessageActionButtons <https://conversejs.org/docs/html/api/-_converse.html#event:getMessageActionButtons>`_
|
||||||
|
which allows you to add, modify or delete the actions you can take on a message
|
||||||
|
in a chat.
|
||||||
|
|
||||||
|
The `Actions <https://github.com/conversejs/community-plugins/tree/master/packages/actions>`_
|
||||||
|
3rd party community plugin makes use of this hook to add extra actions such as
|
||||||
|
``like`` or ``dislike`` to chat messages.
|
||||||
|
|
||||||
|
|
||||||
|
An example plugin
|
||||||
|
-----------------
|
||||||
|
|
||||||
Below follows a documented example of a plugin. This is the same code that gets
|
Below follows a documented example of a plugin. This is the same code that gets
|
||||||
generated by `generator-conversejs <https://github.com/jcbrand/generator-conversejs>`_.
|
generated by `generator-conversejs <https://github.com/jcbrand/generator-conversejs>`_.
|
||||||
@ -511,8 +604,8 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
|
|||||||
* _converse.api.promises.add('myPromise');
|
* _converse.api.promises.add('myPromise');
|
||||||
*
|
*
|
||||||
* Your plugin should then, when appropriate, resolve the
|
* Your plugin should then, when appropriate, resolve the
|
||||||
* promise by calling `_converse.api.emit`, which will also
|
* promise by calling `_converse.api.trigger`, which will also
|
||||||
* emit an event with the same name as the promise.
|
* trigger an event with the same name as the promise.
|
||||||
* For example:
|
* For example:
|
||||||
*
|
*
|
||||||
* _converse.api.trigger('operationCompleted');
|
* _converse.api.trigger('operationCompleted');
|
||||||
@ -526,53 +619,22 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
|
|||||||
*
|
*
|
||||||
* _converse.api.waitUntil('operationCompleted', function { ... });
|
* _converse.api.waitUntil('operationCompleted', function { ... });
|
||||||
*/
|
*/
|
||||||
},
|
|
||||||
|
|
||||||
/* If you want to override some function or a model or
|
|
||||||
* view defined elsewhere in Converse, then you do that under
|
/* In your plugin, you can also listen for hooks.
|
||||||
* the "overrides" namespace.
|
* Hooks allow you to add or modify data and properties used by
|
||||||
|
* Converse.
|
||||||
|
*
|
||||||
|
* For example, the getToolbarButtons hook allows you to add new buttons to the chat toolbar.
|
||||||
|
* https://conversejs.org/docs/html/api/-_converse.html#event:getToolbarButtons
|
||||||
*/
|
*/
|
||||||
overrides: {
|
api.listen.on('getToolbarButtons', (toolbar_el, buttons) => {
|
||||||
/* For example, the private *_converse* object has a
|
buttons.push(html`
|
||||||
* method "onConnected". You can override that method as follows:
|
<button class="my-button" @click=${alert('hello world!')}>
|
||||||
*/
|
<converse-icon class="fa fa-eye" size="1em" color="blue"></converse-icon>
|
||||||
onConnected: function () {
|
</button>
|
||||||
// Overrides the onConnected method in Converse
|
`);
|
||||||
|
return buttons;
|
||||||
// Top-level functions in "overrides" are bound to the
|
});
|
||||||
// inner "_converse" object.
|
|
||||||
const _converse = this;
|
|
||||||
|
|
||||||
// Your custom code can come here ...
|
|
||||||
|
|
||||||
// You can access the original function being overridden
|
|
||||||
// via the __super__ attribute.
|
|
||||||
// Make sure to pass on the arguments supplied to this
|
|
||||||
// function and also to apply the proper "this" object.
|
|
||||||
_converse.__super__.onConnected.apply(this, arguments);
|
|
||||||
|
|
||||||
// Your custom code can come here ...
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Override Converse's XMPPStatus model so that we can override the
|
|
||||||
* function that sends out the presence stanza.
|
|
||||||
*/
|
|
||||||
XMPPStatus: {
|
|
||||||
sendPresence: function (type, status_message, jid) {
|
|
||||||
// The "_converse" object is available via the __super__
|
|
||||||
// attribute.
|
|
||||||
const _converse = this.__super__._converse;
|
|
||||||
|
|
||||||
// Custom code can come here ...
|
|
||||||
|
|
||||||
// You can call the original overridden method, by
|
|
||||||
// accessing it via the __super__ attribute.
|
|
||||||
// When calling it, you need to apply the proper
|
|
||||||
// context as reference by the "this" variable.
|
|
||||||
this.__super__.sendPresence.apply(this, arguments);
|
|
||||||
|
|
||||||
// Custom code can come here ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -21,8 +21,9 @@ Let your XMPP server serve Converse for you
|
|||||||
If you run your own XMPP server, you might first want to check whether it has
|
If you run your own XMPP server, you might first want to check whether it has
|
||||||
a plugin or module for hosting Converse.
|
a plugin or module for hosting Converse.
|
||||||
|
|
||||||
* OpenFire has the `inverse <https://www.igniterealtime.org/projects/openfire/plugin-archive.jsp?plugin=inverse>`_ plugin.
|
* `Openfire <http://www.igniterealtime.org/projects/openfire/>`_ has the `inverse <https://www.igniterealtime.org/projects/openfire/plugin-archive.jsp?plugin=inverse>`_ plugin.
|
||||||
* Prosody has `mod_conversejs <https://modules.prosody.im/mod_conversejs.html>`_.
|
* `Prosody <https://prosody.im/>`_ has `mod_conversejs <https://modules.prosody.im/mod_conversejs.html>`_.
|
||||||
|
* `ejabberd <http://www.ejabberd.im/>`_ has `mod_conversejs <https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs>`_.
|
||||||
|
|
||||||
|
|
||||||
Serving Converse yourself
|
Serving Converse yourself
|
||||||
@ -55,16 +56,16 @@ might break when a new backwards-incompatible version of Converse is released.
|
|||||||
|
|
||||||
To load a specific version of Converse you can put the version in the URL:
|
To load a specific version of Converse you can put the version in the URL:
|
||||||
|
|
||||||
* https://cdn.conversejs.org/8.0.1/dist/converse.min.js
|
* https://cdn.conversejs.org/10.1.6/dist/converse.min.js
|
||||||
* https://cdn.conversejs.org/8.0.1/dist/converse.min.css
|
* https://cdn.conversejs.org/10.1.6/dist/converse.min.css
|
||||||
|
|
||||||
You can include these two URLs inside the *<head>* element of your website
|
You can include these two URLs inside the *<head>* element of your website
|
||||||
via the *script* and *link* tags:
|
via the *script* and *link* tags:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/8.0.1/dist/converse.min.css">
|
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css">
|
||||||
<script src="https://cdn.conversejs.org/8.0.1/dist/converse.min.js" charset="utf-8"></script>
|
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
Option 2: Download the builds from Github
|
Option 2: Download the builds from Github
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/security.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
=======================
|
=======================
|
||||||
Security considerations
|
Security considerations
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/session.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
.. _what-you-will-need:
|
|
||||||
|
|
||||||
==================
|
==================
|
||||||
Session Management
|
Session Management
|
||||||
@ -204,5 +202,4 @@ Storing the SASL SCRAM-SHA1 hash in IndexedDB
|
|||||||
Another suggestion that's been suggested is to store the SCRAM-SHA1 computed
|
Another suggestion that's been suggested is to store the SCRAM-SHA1 computed
|
||||||
``clientKey`` in localStorage and to use that upon page reload to log the user in again.
|
``clientKey`` in localStorage and to use that upon page reload to log the user in again.
|
||||||
|
|
||||||
We might implement this feature in core Converse.js eventually.
|
This has been implemented since version 10, see documentation on `reuse_scram_keys <https://conversejs.org/docs/html/configuration.html#reuse-scram-keys>`_
|
||||||
As always, contributions welcome!
|
|
||||||
|
140
docs/source/setup_dev_environment.rst
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup_dev_environment.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
|
.. _`setup_dev_environment`:
|
||||||
|
|
||||||
|
============================
|
||||||
|
Setting up a dev environment
|
||||||
|
============================
|
||||||
|
|
||||||
|
Installing the 3rd party dependencies
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
To develop and customize Converse, you'll first need to check out Converse's Git
|
||||||
|
repository:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
git clone https://github.com/conversejs/converse.js.git
|
||||||
|
cd converse.js
|
||||||
|
|
||||||
|
|
||||||
|
We use development tools which depend on Node.js and NPM (the Node package manager).
|
||||||
|
|
||||||
|
It's recommended that you use `NVM <https://github.com/nvm-sh/nvm>`_ (the Node version manager)
|
||||||
|
to make sure you have the right version of Node.
|
||||||
|
|
||||||
|
Refer to the `NVM Github page <https://github.com/nvm-sh/nvm#install--update-script>`_ for instructions on how to install it.
|
||||||
|
|
||||||
|
Once NVM is installed, you can run the following inside your checkout of the Converse Git repository:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
nvm install
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
You will always have to first run ``nvm install`` in a new terminal session in order to use the
|
||||||
|
recommended version of Node before working on Converse.
|
||||||
|
|
||||||
|
|
||||||
|
To set up the Converse development environment, you now run ``make dev``.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
make dev
|
||||||
|
|
||||||
|
Alternatively, if you're using Windows, or don't have GNU Make installed, you can run the
|
||||||
|
following:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
npm install
|
||||||
|
npm run lerna
|
||||||
|
|
||||||
|
This will install the Node development tools and Converse's dependencies.
|
||||||
|
|
||||||
|
The front-end dependencies are those JavaScript files on which
|
||||||
|
Converse directly depends and which will be loaded in the browser as part of
|
||||||
|
the bundle in ``dist/converse.js`` (or ``dist/converse.min.js``).
|
||||||
|
|
||||||
|
To see the 3rd party dependencies (not just the front-end dependencies, but
|
||||||
|
also ones necessary for development tasks like making builds), take a look at
|
||||||
|
the list under the ``devDependencies`` in `package.json <https://github.com/jcbrand/converse.js/blob/master/package.json>`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
After running ```make dev```, you should now have a new *node_modules* directory
|
||||||
|
which contains all the external dependencies of Converse.
|
||||||
|
If this directory does NOT exist, something must have gone wrong.
|
||||||
|
Double-check the output of ```make dev``` to see if there are any errors
|
||||||
|
listed. For support, you can ask in our chatroom: `dicuss@conference.conversejs.org <xmpp:discuss@conference.conversejs.org>`_.
|
||||||
|
|
||||||
|
If you don't have an XMPP client installed, follow this link to
|
||||||
|
`conversejs.org <https://conversejs.org/fullscreen#converse/room?jid=discuss@conference.conversejs.org>`_
|
||||||
|
where you can log in and be taken directly to the chatroom.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`dependency-libsignal`:
|
||||||
|
|
||||||
|
Libsignal
|
||||||
|
---------
|
||||||
|
|
||||||
|
If you want OMEMO encryption, you need to load `libsignal <https://github.com/signalapp/libsignal-protocol-javascript>`_ separately in your page.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
<script src="3rdparty/libsignal-protocol-javascript/dist/libsignal-protocol.js"></script>
|
||||||
|
|
||||||
|
The reason libsignal needs to be loaded separately is because it's released
|
||||||
|
under the `GPLv3 <https://github.com/signalapp/libsignal-protocol-javascript/blob/master/LICENSE>`_
|
||||||
|
which requires all other dependent JavaScript code to also be open sourced under the same
|
||||||
|
license. You might not be willing to adhere to those terms, which is why you
|
||||||
|
need to decide for yourself whether you're going to load libsignal or not.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`webserver`:
|
||||||
|
|
||||||
|
Setting up a webserver
|
||||||
|
======================
|
||||||
|
|
||||||
|
When making changes to Converse, either development or theming changes,
|
||||||
|
you'll want to preview them in your browser.
|
||||||
|
|
||||||
|
For this, you'll need to serve the development files via a web server,
|
||||||
|
so that you can see your local changes in the browser.
|
||||||
|
|
||||||
|
Manually starting a web server
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
To both set up the development environment and also start up a web browser to
|
||||||
|
serve the files for you, you can run::
|
||||||
|
|
||||||
|
make serve
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
||||||
|
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
||||||
|
available via your package manager. For Mac, you'll need to install XCode and in
|
||||||
|
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
||||||
|
|
||||||
|
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
||||||
|
|
||||||
|
When developing or changing the theme, you'll want to load all the
|
||||||
|
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
||||||
|
|
||||||
|
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
||||||
|
how ``converse.initialize`` is called and to potentially change any of the
|
||||||
|
settings.
|
||||||
|
|
||||||
|
Starting a web server with live reloading
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
||||||
|
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
||||||
|
|
||||||
|
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
||||||
|
is now being used as the HTML template, and you'll need to modify that file if
|
||||||
|
you want to change the settings passed to ``converse.initialize``.
|
||||||
|
|
||||||
|
If you're running ``make devserver``, you need to open http://localhost:8080.
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/style_guide.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
Software Style Guide
|
Software Style Guide
|
||||||
====================
|
====================
|
||||||
@ -11,7 +11,7 @@ Most of the style guide recommendations here come from Douglas Crockford's book
|
|||||||
Tabs or spaces?
|
Tabs or spaces?
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
We always indent 4 spaces. Proper indentation is important for readability.
|
We always indent 4 spaces.
|
||||||
|
|
||||||
Underscores or camelCase?
|
Underscores or camelCase?
|
||||||
-------------------------
|
-------------------------
|
||||||
@ -27,6 +27,12 @@ For example:
|
|||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const versus let
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Try to use `const` whenever possible. If a variable won't be reassigned, use
|
||||||
|
`const`, otherwise use `let`.
|
||||||
|
|
||||||
Spaces around operators
|
Spaces around operators
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
@ -51,6 +57,19 @@ An exception is when they appear inside for-loop expressions, for example:
|
|||||||
Generally though, rather err on the side of adding spaces, since they make the
|
Generally though, rather err on the side of adding spaces, since they make the
|
||||||
code much more readable.
|
code much more readable.
|
||||||
|
|
||||||
|
destructuring
|
||||||
|
-------------
|
||||||
|
|
||||||
|
When assigning to a variable via destructuring, add spaces between the curly
|
||||||
|
brackets.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
const { foo } = bar;
|
||||||
|
|
||||||
|
|
||||||
Global constants are written in ALL_CAPS
|
Global constants are written in ALL_CAPS
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
@ -11,19 +11,109 @@ Theming
|
|||||||
Setting up your environment
|
Setting up your environment
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
In order to theme Converse, you first need to set up a :ref:`development` environment
|
In order to theme Converse, you first need to follow the steps for :ref:`setup_dev_environment`, including :ref:`webserver`.
|
||||||
and then you'll also want to follow the guide to :ref:`webserver`.
|
|
||||||
|
Creating a custom theme
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Converse can be themed via CSS custom properties (aka CSS variables) and it has
|
||||||
|
some themes available in its source repository.
|
||||||
|
|
||||||
|
A theme is a CSS file with a specific rule that defines the theme's CSS properties.
|
||||||
|
The rule has a specific selector that must include (and determines) the theme name.
|
||||||
|
|
||||||
|
Inside this CSS rule, various CSS variables are assigned values.
|
||||||
|
The CSS variables mainly refer to the colors that comprise the theme.
|
||||||
|
If you don't specify a value for a specific CSS variable, then the value from
|
||||||
|
the ``classic`` theme is used, as defined in `classic.scss <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes/classic.scss>`_.
|
||||||
|
|
||||||
|
The native theme files can be found in `shared/styles/themes <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes>`_.
|
||||||
|
|
||||||
|
Note, the Converse theme files have a ``.scss`` extension because they are compiled
|
||||||
|
by the Sass compiler into normal CSS files. It's however not necessary to use
|
||||||
|
Sass, basic CSS files will also suffice.
|
||||||
|
|
||||||
|
The theme that Converse uses can be set via the :ref:`theme` configuration
|
||||||
|
setting (and the :ref:`dark_theme` configuration setting for dark mode).
|
||||||
|
|
||||||
|
How are themes applied?
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
When you set a value for the :ref:`theme` configuration setting, Converse will add
|
||||||
|
a class ``theme-${api.settings.get('theme')}`` on the ``converse-root`` DOM
|
||||||
|
element.
|
||||||
|
|
||||||
|
So, for example, if you set the ``theme`` setting to ``"dracula"``, then the
|
||||||
|
``converse-root`` element will get the class ``theme-dracula``.
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
converse.initialize({ theme: "dracula" });
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<converse-root class="conversejs theme-dracula"></converse-root>
|
||||||
|
|
||||||
|
|
||||||
|
The apply a theme, there then needs to be a CSS rule with a selector that matches the
|
||||||
|
``theme-dracula`` class on the ``converse-root`` element.
|
||||||
|
|
||||||
|
If you take a look at the theme file `dracula.scss <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes/dracula.scss>`_
|
||||||
|
you'll see that it defines a CSS rule with the selector
|
||||||
|
``.conversejs.theme-dracula``.
|
||||||
|
|
||||||
|
This selector matches any DOM element with both the classes ``.conversejs`` and
|
||||||
|
``.theme-dracula``. The ``converse-root`` element will already have the class
|
||||||
|
``.conversejs`` and it will have the class ``.theme-dracula`` if the ``theme``
|
||||||
|
(or ``dark_theme`` in dark mode) configuration setting is set to ``"dracula"``.
|
||||||
|
|
||||||
|
This is how themes are applied, by defining a CSS selector that matches the
|
||||||
|
class ``.theme-${name}`` (where ``name`` is a variable containing the name of
|
||||||
|
the theme), and then setting the ``theme`` (and/or ``dark_theme``) configuration
|
||||||
|
setting.
|
||||||
|
|
||||||
|
To create your own theme, you can create a similar CSS rule that matches
|
||||||
|
your theme's name and then you set the ``theme`` configuration setting to that
|
||||||
|
name. This CSS rule can be in any CSS file that is loaded in your website, or
|
||||||
|
you can even put it in the DOM as an inline style.
|
||||||
|
|
||||||
|
Modifying the CSS
|
||||||
|
=================
|
||||||
|
|
||||||
|
To create a new theme with different colors, it should be enough to create a
|
||||||
|
theme file that sets the various CSS variables (as described above).
|
||||||
|
|
||||||
|
For other CSS-related changes, you can make a specific
|
||||||
|
CSS rule with that matches the element you want to change.
|
||||||
|
|
||||||
|
Sometimes it might however be neccessary to modify the core CSS files from
|
||||||
|
Converse, for example if you're developing new features or fixing styling bugs.
|
||||||
|
|
||||||
|
The CSS files are generated from `Sass <http://sass-lang.com>`_ files that end in ``.scss`` and
|
||||||
|
which are distributed throughout the source code.
|
||||||
|
|
||||||
|
The CSS that is relevant to a particular plugin
|
||||||
|
is usually inside the ``./styles`` directory inside the relevant plugin directory.
|
||||||
|
|
||||||
|
For example: `src/plugins/controlbox/styles <https://github.com/conversejs/converse.js/tree/master/src/plugins/controlbox/styles>`_.
|
||||||
|
|
||||||
|
If you're running ``make watch``, then the CSS will automatically be
|
||||||
|
regenerated when you've changed any of the ``.scss``.
|
||||||
|
|
||||||
|
You can also manually generate the CSS::
|
||||||
|
|
||||||
|
make css
|
||||||
|
|
||||||
Modifying the HTML templates of Converse
|
Modifying the HTML templates of Converse
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_ as HTML
|
Converse uses `lit-html <https://lit.dev/docs/libraries/standalone-templates/>`_ as HTML
|
||||||
templating library, and the HTML source code is contained in JavaScript ``.js``
|
templating library, and the HTML source code is contained in JavaScript ``.js``
|
||||||
files in various ``./template`` directories in the source code.
|
files in various ``./template`` directories in the source code.
|
||||||
|
|
||||||
Some top-level templates are found in the ``./src/templates`` directory, but
|
Some top-level templates are also in the ``./src/templates`` directory, but
|
||||||
usually the templates that are relevant to a specific plugin will be find
|
the templates that are relevant to a specific plugin should be inside that plugin's subdirectory.
|
||||||
inside that plugin's subdirectory.
|
|
||||||
|
|
||||||
For example: `src/plugins/chatview/templates <https://github.com/conversejs/converse.js/tree/master/src/plugins/chatview/templates>`_.
|
For example: `src/plugins/chatview/templates <https://github.com/conversejs/converse.js/tree/master/src/plugins/chatview/templates>`_.
|
||||||
|
|
||||||
@ -53,19 +143,3 @@ two of my own custom templates.
|
|||||||
'./templates/message.js': path.resolve(__dirname, 'path/to/my/custom/chat_message.js'),
|
'./templates/message.js': path.resolve(__dirname, 'path/to/my/custom/chat_message.js'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Modifying the CSS
|
|
||||||
=================
|
|
||||||
|
|
||||||
The CSS files are generated from `Sass <http://sass-lang.com>`_ files that end in ``.scss`` and
|
|
||||||
which are distributed throughout the source code.
|
|
||||||
|
|
||||||
Similarly to the template files, the CSS that is relevant to a particular plugin
|
|
||||||
is usually inside the ``./styles`` directory inside the relevant plugin
|
|
||||||
directory.
|
|
||||||
|
|
||||||
For example: `src/plugins/controlbox/styles <https://github.com/conversejs/converse.js/tree/master/src/plugins/controlbox/styles>`_.
|
|
||||||
|
|
||||||
To generate the CSS you can run::
|
|
||||||
|
|
||||||
make css
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup.rst">Edit me on GitHub</a></div>
|
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/troubleshooting.rst">Edit me on GitHub</a></div>
|
||||||
|
|
||||||
=============================
|
=============================
|
||||||
Troubleshooting and debugging
|
Troubleshooting and debugging
|
||||||
@ -114,6 +114,89 @@ what you're using as the HTTP file server.
|
|||||||
CORS is enabled by adding an ``Access-Control-Allow-Origin`` header, so you'll
|
CORS is enabled by adding an ``Access-Control-Allow-Origin`` header, so you'll
|
||||||
have to configure your file server to add this header.
|
have to configure your file server to add this header.
|
||||||
|
|
||||||
|
Users don't stay logged in across page reloads
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
A common complaint in the Converse chat room (`<xmpp:discuss@conference.conversejs.org?join>`_)
|
||||||
|
is that users are logged out when they reload the page.
|
||||||
|
|
||||||
|
The main way in which websites and web apps maintain a user's login session is via
|
||||||
|
authentication cookies, which are included in every HTTP request sent to the server.
|
||||||
|
|
||||||
|
XMPP is however not HTTP, cookies aren't automatically included in traffic to
|
||||||
|
the XMPP server and XMPP servers don't rely on cookies for authentication.
|
||||||
|
|
||||||
|
Instead, an XMPP client is expected to store the user credentials (username and
|
||||||
|
password, either plaintext or hashed and salted if
|
||||||
|
`SCRAM <https://en.wikipedia.org/wiki/Salted_Challenge_Response_Authentication_Mechanism>`_
|
||||||
|
is being used) and to then present those credentials to the XMPP server when authenticating.
|
||||||
|
|
||||||
|
This works well for non-web XMPP clients, but Converse has so far avoided
|
||||||
|
storing user credentials in browser storage, since they can then be accessed by
|
||||||
|
any scripts running in the browser under the same domain.
|
||||||
|
|
||||||
|
So what does Converse do to keep users logged in?
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
Use the Web Auth API
|
||||||
|
********************
|
||||||
|
|
||||||
|
Converse supports the `Web Authentication API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API>`_
|
||||||
|
which let's it use the secure credential management of the browser to get the
|
||||||
|
uesr credentials to automatically log the user in. This however requires that
|
||||||
|
the user saves his or her username and password in the browser. Often the user
|
||||||
|
is automatically asked by the browser whether he/she wants to store the
|
||||||
|
credentials. If that doesn't happen, the user has to do so manually, usually by
|
||||||
|
clicking the key icon in the address bar. This works well on most modern browsers,
|
||||||
|
but not on Firefox, which has insufficient support for the Web Authentication API.
|
||||||
|
|
||||||
|
What can users do to stay logged in?
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Outsource credential management to something else
|
||||||
|
*************************************************
|
||||||
|
|
||||||
|
The issues mentioned above mostly related to users logging in manually, and not
|
||||||
|
to integrations where Converse automatically fetches user credentials from the
|
||||||
|
backend via the :ref:`credentials_url` setting.
|
||||||
|
|
||||||
|
Use BOSH instead of websocket
|
||||||
|
*****************************
|
||||||
|
|
||||||
|
`BOSH <https://xmpp.org/extensions/xep-0206.html>`_ can be thought of
|
||||||
|
XMPP-over-HTTP and because HTTP is stateless, BOSH needs to maintain login
|
||||||
|
sessions for a certain amount of time (usually 60 seconds) even if there is no
|
||||||
|
HTTP traffic between the client and server. This means that if you have a BOSH
|
||||||
|
session running, you can reload the page and you will stay logged in.
|
||||||
|
|
||||||
|
Note, Websocket connections are however faster and have less overhead than BOSH.
|
||||||
|
|
||||||
|
User a browser with adequate support for the Web Auth API
|
||||||
|
*********************************************************
|
||||||
|
|
||||||
|
Another option is to only use a browser with proper support for the Web Auth
|
||||||
|
API (which mainly means avoiding Firefox) and then to save your credentials in the browser.
|
||||||
|
|
||||||
|
Use Converse Desktop
|
||||||
|
********************
|
||||||
|
|
||||||
|
The `desktop version of Converse <https://github.com/conversejs/converse-desktop>`_
|
||||||
|
also doesn't have this problem, since the credentials are stored in Electron
|
||||||
|
and there is no significant risk of other malicious scripts running.
|
||||||
|
|
||||||
|
What else can Converse do to keep users logged in?
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
This problem could also potentially be fixed by storing the
|
||||||
|
XMPP credentials securely with web crypto and IndexedDB. This could be done by
|
||||||
|
generating a private encryption key in non-exportable format, and then using that
|
||||||
|
to encrypt the credentials before storing them in IndexedDB.
|
||||||
|
|
||||||
|
This would protect the credentials from someone who has access to your
|
||||||
|
computer (or harddrive), but it still won't protect them from malicious scripts
|
||||||
|
running in the same domain as Converse is being hosted, since they would have the
|
||||||
|
same level of access as Converse itself (which legitimately needs access to the
|
||||||
|
credentials).
|
||||||
|
|
||||||
Common errors
|
Common errors
|
||||||
=============
|
=============
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
.. raw:: html
|
|
||||||
|
|
||||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/webserver.rst">Edit me on GitHub</a></div>
|
|
||||||
|
|
||||||
|
|
||||||
.. _`webserver`:
|
|
||||||
|
|
||||||
Setting up a webserver
|
|
||||||
======================
|
|
||||||
|
|
||||||
When making changes to Converse, either development or theming changes,
|
|
||||||
you'll want to preview them in your browser.
|
|
||||||
|
|
||||||
For this, you'll need to serve the development files via a web server,
|
|
||||||
so that you can see your local changes in the browser.
|
|
||||||
|
|
||||||
Manually starting a web server
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
To both set up the development environment and also start up a web browser to
|
|
||||||
serve the files for you, you can run::
|
|
||||||
|
|
||||||
make serve
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
|
||||||
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
|
||||||
available via your package manager. For Mac, you'll need to install XCode and in
|
|
||||||
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
|
||||||
|
|
||||||
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
|
||||||
|
|
||||||
When developing or changing the theme, you'll want to load all the
|
|
||||||
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
|
||||||
|
|
||||||
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
|
||||||
how ``converse.initialize`` is called and to potentially change any of the
|
|
||||||
settings.
|
|
||||||
|
|
||||||
If you're running ``make devserver``, you need to open http://localhost:8080
|
|
||||||
instead.
|
|
||||||
|
|
||||||
Starting a web server with live reloading
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
|
||||||
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
|
||||||
|
|
||||||
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
|
||||||
is now being used as the HTML template, and you'll need to modify that file if
|
|
||||||
you want to change the settings passed to ``converse.initialize``.
|
|
@ -3,6 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Converse</title>
|
<title>Converse</title>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
||||||
<meta name="author" content="JC Brand" />
|
<meta name="author" content="JC Brand" />
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<title>Converse</title>
|
<title>Converse</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
||||||
<meta name="author" content="JC Brand" />
|
<meta name="author" content="JC Brand" />
|
||||||
@ -143,6 +144,7 @@
|
|||||||
<li style="list-style: none">XMPP Servers</li>
|
<li style="list-style: none">XMPP Servers</li>
|
||||||
<li><a href="https://www.igniterealtime.org/projects/openfire/plugins.jsp" target="_blank" rel="noopener">Openfire</a></li>
|
<li><a href="https://www.igniterealtime.org/projects/openfire/plugins.jsp" target="_blank" rel="noopener">Openfire</a></li>
|
||||||
<li><a href="https://modules.prosody.im/mod_conversejs.html" target="_blank" rel="noopener">Prosody</a></li>
|
<li><a href="https://modules.prosody.im/mod_conversejs.html" target="_blank" rel="noopener">Prosody</a></li>
|
||||||
|
<li><a href="https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs" target="_blank" rel="noopener">Ejabberd</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="integration">
|
<ul class="integration">
|
||||||
<li style="list-style: none">Frameworks</li>
|
<li style="list-style: none">Frameworks</li>
|
||||||
@ -238,12 +240,11 @@
|
|||||||
<div class="sponsors">
|
<div class="sponsors">
|
||||||
<h2>Converse is supported by:</h2>
|
<h2>Converse is supported by:</h2>
|
||||||
<ul >
|
<ul >
|
||||||
|
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 13em" src="/media/logos/bairesdev-primary.png" alt="BairesDev"></a></li>
|
||||||
|
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
||||||
|
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||||
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
||||||
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
||||||
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
|
|
||||||
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
|
||||||
<li><a href="https://www.pluckeye.net/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/logo/pluckeye.svg" alt="Pluckeye"></a></li>
|
|
||||||
<li><a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/logo/originalenergie.png" alt="Original Energie"></a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
<!-- These files are NOT needed when using converse.js in your own project. -->
|
<!-- These files are NOT needed when using converse.js in your own project. -->
|
||||||
<link rel="shortcut icon" type="image/ico" href="images/favicon.ico"/>
|
<link rel="shortcut icon" type="image/ico" href="images/favicon.ico"/>
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/8.0.1/css/font-awesome.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/font-awesome.min.css" />
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/8.0.1/css/website.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/website.min.css" />
|
||||||
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
||||||
<script type="text/javascript" src="/src/website.js"></script>
|
<script type="text/javascript" src="/src/website.js"></script>
|
||||||
<script type="text/javascript" src="analytics.js"></script>
|
<script type="text/javascript" src="analytics.js"></script>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
||||||
<![if gte IE 11]>
|
<![if gte IE 11]>
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/8.0.1/css/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/converse.min.css" />
|
||||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
<script src="https://cdn.conversejs.org/8.0.1/dist/converse.min.js"></script>
|
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
||||||
<![endif]>
|
<![endif]>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
<table id="jslicense-labels1" style="width: 100%">
|
<table id="jslicense-labels1" style="width: 100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://cdn.conversejs.org/8.0.1/dist/converse.min.js">converse.min.js</a>
|
<a href="https://cdn.conversejs.org/10.1.6/dist/converse.min.js">converse.min.js</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.mozilla.org/en-US/MPL/2.0/">MPL-2.0</a>
|
<a href="https://www.mozilla.org/en-US/MPL/2.0/">MPL-2.0</a>
|
||||||
|
@ -3,14 +3,15 @@ const path = require('path');
|
|||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
config.set({
|
config.set({
|
||||||
|
|
||||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||||
basePath: '',
|
basePath: '',
|
||||||
frameworks: ['jasmine'],
|
frameworks: ['jasmine'],
|
||||||
files: [
|
files: [
|
||||||
{ pattern: 'dist/*.js.map', included: false },
|
{ pattern: 'dist/*.js.map', included: false },
|
||||||
{ pattern: 'dist/*.css.map', included: false },
|
{ pattern: 'dist/*.css.map', included: false },
|
||||||
{ pattern: "dist/icons.js", served: true },
|
|
||||||
{ pattern: "dist/emojis.js", served: true },
|
{ pattern: "dist/emojis.js", served: true },
|
||||||
|
"src/shared/tests/tests.css",
|
||||||
"node_modules/lodash/lodash.min.js",
|
"node_modules/lodash/lodash.min.js",
|
||||||
"dist/converse.js",
|
"dist/converse.js",
|
||||||
"dist/converse.css",
|
"dist/converse.css",
|
||||||
@ -28,16 +29,21 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/headless/plugins/chat/tests/api.js", type: 'module' },
|
{ pattern: "src/headless/plugins/chat/tests/api.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/disco/tests/disco.js", type: 'module' },
|
{ pattern: "src/headless/plugins/disco/tests/disco.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/muc/tests/affiliations.js", type: 'module' },
|
{ pattern: "src/headless/plugins/muc/tests/affiliations.js", type: 'module' },
|
||||||
|
{ pattern: "src/headless/plugins/muc/tests/messages.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/muc/tests/muc.js", type: 'module' },
|
{ pattern: "src/headless/plugins/muc/tests/muc.js", type: 'module' },
|
||||||
|
{ pattern: "src/headless/plugins/muc/tests/occupants.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/muc/tests/pruning.js", type: 'module' },
|
{ pattern: "src/headless/plugins/muc/tests/pruning.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/muc/tests/registration.js", type: 'module' },
|
{ pattern: "src/headless/plugins/muc/tests/registration.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/ping/tests/ping.js", type: 'module' },
|
{ pattern: "src/headless/plugins/ping/tests/ping.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/roster/tests/presence.js", type: 'module' },
|
{ pattern: "src/headless/plugins/roster/tests/presence.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/smacks/tests/smacks.js", type: 'module' },
|
{ pattern: "src/headless/plugins/smacks/tests/smacks.js", type: 'module' },
|
||||||
{ pattern: "src/headless/plugins/status/tests/status.js", type: 'module' },
|
{ pattern: "src/headless/plugins/status/tests/status.js", type: 'module' },
|
||||||
|
{ pattern: "src/headless/shared/settings/tests/settings.js", type: 'module' },
|
||||||
{ pattern: "src/headless/tests/converse.js", type: 'module' },
|
{ pattern: "src/headless/tests/converse.js", type: 'module' },
|
||||||
{ pattern: "src/headless/tests/eventemitter.js", type: 'module' },
|
{ pattern: "src/headless/tests/eventemitter.js", type: 'module' },
|
||||||
{ pattern: "src/modals/tests/user-details-modal.js", type: 'module' },
|
{ pattern: "src/modals/tests/user-details-modal.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/adhoc-views/tests/adhoc.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/bookmark-views/tests/bookmarks-list.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/bookmark-views/tests/bookmarks.js", type: 'module' },
|
{ pattern: "src/plugins/bookmark-views/tests/bookmarks.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/chatview/tests/chatbox.js", type: 'module' },
|
{ pattern: "src/plugins/chatview/tests/chatbox.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/chatview/tests/corrections.js", type: 'module' },
|
{ pattern: "src/plugins/chatview/tests/corrections.js", type: 'module' },
|
||||||
@ -63,22 +69,29 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/plugins/mam-views/tests/placeholder.js", type: 'module' },
|
{ pattern: "src/plugins/mam-views/tests/placeholder.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/minimize/tests/minchats.js", type: 'module' },
|
{ pattern: "src/plugins/minimize/tests/minchats.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/autocomplete.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/autocomplete.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/mep.js", type: 'module' },
|
|
||||||
{ pattern: "src/plugins/muc-views/tests/component.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/component.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/corrections.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/corrections.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/disco.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/emojis.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/emojis.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/hats.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/hats.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/http-file-upload.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/http-file-upload.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/info-messages.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/mam.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/markers.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/markers.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/me-messages.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/me-messages.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/member-lists.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/mentions.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/mentions.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/mep.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/modtools.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/modtools.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/muc-add-modal.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muc-api.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/muc-api.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/muc-list-modal.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muc-mentions.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/muc-mentions.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muc-messages.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/muc-messages.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muc-registration.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/muc-registration.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muc.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/muc.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/muclist.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/nickname.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/muc-views/tests/occupants.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/retractions.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/retractions.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/styling.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/styling.js", type: 'module' },
|
||||||
@ -86,11 +99,18 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/plugins/muc-views/tests/unfurls.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/unfurls.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/xss.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/xss.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/notifications/tests/notification.js", type: 'module' },
|
{ pattern: "src/plugins/notifications/tests/notification.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/omemo/tests/corrections.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/omemo/tests/muc.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/profile/tests/password-reset.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/profile/tests/profile.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/profile/tests/status.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/push/tests/push.js", type: 'module' },
|
{ pattern: "src/plugins/push/tests/push.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/rosterview/tests/add-contact-modal.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/presence.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/presence.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/protocol.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/protocol.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/roster.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/roster.js", type: 'module' },
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"packages": [
|
|
||||||
".",
|
|
||||||
"src/*"
|
|
||||||
],
|
|
||||||
"version": "4.0.3"
|
|
||||||
}
|
|
Before Width: | Height: | Size: 12 KiB |
@ -1,19 +1,34 @@
|
|||||||
<svg class="converse-svg-logo"
|
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg108" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xml:space="preserve" version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
viewBox="0 0 376 311" height="20%" width="10rem"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
||||||
viewBox="0 0 364 364">
|
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview16" showgrid="false" inkscape:zoom="0.75884244" inkscape:cx="188" inkscape:cy="155.5" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg108"/>
|
||||||
<title>Converse</title>
|
<metadata id="metadata114">
|
||||||
<g class="cls-1" id="g904">
|
<rdf:RDF>
|
||||||
<g data-name="Layer 2">
|
<cc:Work rdf:about="">
|
||||||
<g data-name="Layer 7">
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<path
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
class="cls-3"
|
<dc:title/>
|
||||||
d="M221.46,103.71c0,18.83-29.36,18.83-29.12,0C192.1,84.88,221.46,84.88,221.46,103.71Z" />
|
<cc:license rdf:resource="GFDL version 1.3 ou ultérieure, Creative Commons By Sa version 2.0 ou ultérieure, Licence Art Libre version 1.3 ou ultérieure"/>
|
||||||
<path
|
<dc:creator>
|
||||||
class="cls-4"
|
<cc:Agent>
|
||||||
d="M179.9,4.15A175.48,175.48,0,1,0,355.38,179.63,175.48,175.48,0,0,0,179.9,4.15Zm-40.79,264.5c-.23-17.82,27.58-17.82,27.58,0S138.88,286.48,139.11,268.65ZM218.6,168.24A79.65,79.65,0,0,1,205.15,174a12.76,12.76,0,0,0-6.29,4.65L167.54,222a1.36,1.36,0,0,1-2.46-.8v-35.8a2.58,2.58,0,0,0-3.06-2.53c-15.43,3-30.23,7.7-42.73,19.94-38.8,38-29.42,105.69,16.09,133.16a162.25,162.25,0,0,1-91.47-67.27C-3.86,182.26,34.5,47.25,138.37,25.66c46.89-9.75,118.25,5.16,123.73,62.83C265.15,120.64,246.56,152.89,218.6,168.24Z" />
|
<dc:title>Antoine BARDELLI</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs id="defs112"/>
|
||||||
|
<g id="g250" style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" transform="translate(4.6079614e-7,-4.4571451e-6)">
|
||||||
|
<path id="path124" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 96.70711,209.69029 c -2.742,1.114 -5.399,1.8 -7.885,2.057 -0.771,0.085 -1.629,0.171 -2.4,0.171 -2.571,0 -5.314,-0.428 -8.228,-1.286 -2.486,-0.857 -4.628,-2.314 -6.514,-4.371 -1.886,-1.8 -3.343,-3.942 -4.371,-6.428 -0.943,-2.486 -1.457,-5.057 -1.543,-7.714 0,-0.086 0,-0.257 0,-0.343 0,-2.485 0.514,-4.971 1.543,-7.371 1.028,-2.314 2.485,-4.457 4.371,-6.428 1.886,-2.057 4.028,-3.514 6.514,-4.371 2.828,-1.029 5.571,-1.543 8.142,-1.543 0.257,0 0.6,0 0.857,0 3.686,0.171 6.857,1.028 9.686,2.657 -0.086,1.457 -0.343,3.514 -0.6,6.343 -2.743,-1.372 -5.4,-2.143 -7.971,-2.315 -1.972,-0.171 -3.943,0.086 -6,0.772 -1.629,0.514 -3.171,1.457 -4.457,2.914 -1.286,1.371 -2.228,2.828 -2.914,4.457 -0.772,1.628 -1.114,3.342 -1.114,5.142 0.085,1.886 0.428,3.6 1.114,5.229 0.686,1.714 1.628,3.171 2.914,4.457 1.286,1.285 2.828,2.228 4.457,2.914 2.486,0.685 4.543,1.028 6.343,1.028 3.342,-0.343 5.999,-1.2 8.056,-2.657 0,1.457 0,3.686 0,6.686 z"/>
|
||||||
|
<path id="path126" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 137.33411,184.91929 c 0,1.8 0,3.515 0,5.314 0,2.657 0,5.4 -0.086,8.057 0,4.457 0,8.743 0,12.771 -1.714,0 -4.285,0 -7.799,0 0,-3.857 0,-11.571 0.085,-23.142 0.086,-1.114 -0.171,-2.228 -0.6,-3.257 -0.428,-1.028 -1.028,-1.971 -1.8,-2.742 -0.942,-0.943 -2.142,-1.715 -3.599,-2.143 -1.029,-0.257 -2.229,-0.429 -3.6,-0.343 -1.115,0.086 -2.229,0.343 -3.257,0.686 -1.029,0.428 -2.143,1.028 -3.343,1.8 -0.6,0.6 -1.029,1.2 -1.457,1.8 v 27.341 h -8.057 v -62.997 h 8.057 v 29.313 c 0.943,-1.114 1.8,-1.971 2.571,-2.486 1.629,-0.942 3,-1.542 4.2,-1.885 1.971,-0.429 3.771,-0.6 5.571,-0.6 2.143,0.085 3.943,0.343 5.4,0.943 1.543,0.6 2.914,1.542 4.028,2.742 2.4,2.572 3.6,5.486 3.686,8.828 z"/>
|
||||||
|
<path id="path128" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 169.38911,146.86429 c 4.714,10.714 14.228,32.056 28.37,64.111 -1.371,0 -4.114,0 -8.313,0 -1.286,-2.657 -3.772,-7.971 -7.543,-15.942 -4.028,0 -12.085,0 -24.17,0 -1.114,2.657 -3.514,7.971 -7.028,15.942 -1.457,0 -4.2,0 -8.4,0 4.543,-10.713 13.542,-32.055 27.084,-64.111 z m 9.171,40.884 c -1.457,-3.686 -4.542,-10.885 -9.085,-21.77 -1.457,3.599 -4.285,10.885 -8.571,21.77 3,0 8.828,0 17.656,0 z"/>
|
||||||
|
<path id="path130" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 212.15911,199.83329 c 0.257,0.429 0.6,0.771 0.857,1.114 1.114,1.457 2.657,2.743 4.457,3.686 1.457,0.685 3.085,1.028 4.8,1.028 0.085,0 0.257,0 0.428,0 1.8,0.086 3.514,-0.257 5.143,-1.028 1.628,-0.6 3.171,-1.629 4.457,-2.914 1.285,-1.286 2.228,-2.743 2.914,-4.457 0.685,-1.543 1.028,-3.343 1.028,-5.314 0.086,-1.715 -0.257,-3.429 -1.028,-5.057 -0.6,-1.629 -1.629,-3.171 -2.914,-4.457 -1.286,-1.286 -2.743,-2.229 -4.457,-2.914 -1.115,-0.515 -2.572,-0.772 -4.2,-0.772 -0.172,0 -0.343,0 -0.429,0 -2.657,0.086 -4.714,0.429 -5.999,1.2 -3.429,1.8 -5.143,3.857 -5.143,6.171 0,3 0,7.629 0.086,13.714 z m 0,8.314 c -0.086,3.343 -0.086,10.028 -0.086,20.056 -1.286,0 -4.028,0 -8.057,0 v -54.769 c 2.657,0 5.4,0 8.057,0 0.086,1.286 0.086,2.4 0.086,3.257 0.857,-1.114 2.314,-2.142 4.457,-3.085 2.4,-1.029 4.971,-1.543 7.714,-1.543 2.657,0 5.228,0.514 7.628,1.543 2.485,1.028 4.714,2.485 6.514,4.371 1.971,1.886 3.428,4.028 4.371,6.428 1.028,2.486 1.543,4.971 1.543,7.543 0,0.085 0,0.085 0,0.171 0,2.657 -0.515,5.228 -1.543,7.714 -1.029,2.486 -2.486,4.628 -4.371,6.428 -1.886,1.972 -4.029,3.429 -6.514,4.371 -2.486,1.029 -5.057,1.543 -7.628,1.543 -2.657,-0.085 -5.229,-0.6 -7.714,-1.543 -1.372,-0.514 -2.829,-1.371 -4.457,-2.485 z"/>
|
||||||
|
<path id="path132" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 271.04211,180.46329 c 0,-0.086 -0.686,-0.172 -2.143,-0.429 -2.743,-0.086 -5.228,0.857 -7.371,2.828 -0.857,0.772 -1.543,1.886 -2.057,3.343 -0.343,1.371 -0.515,2.829 -0.6,4.457 0,4.543 0,11.399 0,20.399 -1.286,0 -3.943,0 -7.971,0 0,-6.257 0,-18.856 -0.086,-37.627 1.286,0 3.943,0 7.8,0 0.085,0.772 0.085,2.4 0.085,4.8 0,0.514 0,1.543 -0.085,3.171 1.028,-4.199 3.085,-6.856 6.085,-7.971 1.2,-0.343 2.4,-0.514 3.686,-0.514 0.857,-0.086 1.714,0 2.657,0.171 0,1.629 0,4.115 0,7.372 z"/>
|
||||||
|
<path id="path134" style="clip-rule:evenodd;fill:#005184;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 277.47011,163.23529 c 0,-1.372 0.514,-2.4 1.457,-3.171 0.943,-0.772 1.971,-1.2 3.085,-1.2 0.086,0 0.172,0 0.258,0 1.114,0 2.057,0.342 2.999,1.028 0.943,0.771 1.372,1.8 1.372,3.257 0,1.457 -0.429,2.571 -1.372,3.428 -0.942,0.858 -2.057,1.286 -3.257,1.286 -1.114,0 -2.142,-0.428 -3.085,-1.2 -0.943,-0.771 -1.457,-1.971 -1.457,-3.428 z"/>
|
||||||
|
<path style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" id="path136" d="m 278.15611,173.34929 c 1.371,0 4.028,0 8.056,0 0,6.342 0,18.942 0,37.883 -1.285,0 -3.942,0 -7.885,0 0,-6.256 -0.086,-18.941 -0.171,-37.883 z"/>
|
||||||
|
<path id="path138" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 295.55511,146.26429 v 64.797 c 1.285,0 3.942,0 7.885,0 0,-10.799 0,-32.398 0,-64.797 -1.286,0 -3.943,0 -7.885,0 z"/>
|
||||||
|
<path id="path144" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 79.90511,45.008286 c 0.902,0.305 0.903,0.306 1.74,0.76 0,0 2.36,1.6 3.541,2.399 16.192,11.056 46.905,36.807 46.905,36.807 l -0.62,0.933 c 0,0 -30.962,-17.269 -47.812,-25.837 -0.154,2.104 -0.309,4.208 -0.465,6.312 l -0.126,1.689 c -0.635,7.993 -1.295,15.97 -2,23.945 l -0.096,1.076 c -0.824,8.583004 -1.124,17.369004 -3.069,25.573004 -1.802,7.601 -10.27,13.477 -15.485,20.114 -10.856,13.816 -19.361,29.582 -21.64,47.603 -2.579,20.389 5.773,40.853 21.062,55.815 5.92,5.794 12.534,10.853 19.323,15.617 l -3.768,5.852 c -20.581,-12.139 -40.182,-28.618 -47.181,-51.981 -6.615,-22.081 -1.608,-47.917 10.144,-67.278 6.689,-11.02 14.834,-21.098 23.413,-30.201 0,0 0.585,-2.521 0.874,-4.27 3.14,-19.016004 5.383,-38.057004 7.92,-57.227004 l 0.274,-2.074 0.28,-1.686 c 0.307,-0.8 0.337,-1.033 0.841,-1.737 1.119,-1.562 2.062,-2.818 5.945,-2.204 z"/>
|
||||||
|
<path id="path146" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 300.65411,44.858286 c 2.74,0.486 2.778,0.936 3.448,1.587 2.555,2.484 2.191,7.238 2.75,11.471 2.467,18.612 4.172,37.353 7.871,55.935004 0,0 3.607,4.013 6.264,7.099 11.064,12.849 21.019,26.411 26.284,43.975 6.27,20.92 3.585,41.82 -7.633,60.315 -9.349,15.414 -24.268,26.668 -39.964,35.962 l -0.593,-1.853 c 17.279,-12.08 34.133,-26.972 39.318,-48.24 3.677,-15.083 -0.809,-32.515 -6.571,-47.292 -5.094,-13.065 -14.589,-24.209 -23.233,-34.394 -1.789,-2.107 -3.603,-4.192 -5.472,-6.229 0,0 -2.204,-3.069 -2.806,-6.739 -2.582,-15.749 -3.416,-31.540004 -4.699,-47.326004 0,0 -0.34,-4.424 -0.589,-7.754 l -0.098,-1.322 -30.582,18.428 -15.221,9.11 -0.801,0.477 -3.955,-6.151 0.767,-0.531 14.605,-10.067 37.28,-25.534 c 0,0 1.172,-0.939 3.63,-0.927 z m 14.362,70.312004 c 0.178,0.639 -0.049,-0.186 0,0 z"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 8.9 KiB |
19
logo/conversejs-transparent.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<svg class="converse-svg-logo"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 364 364">
|
||||||
|
<title>Converse</title>
|
||||||
|
<g class="cls-1" id="g904">
|
||||||
|
<g data-name="Layer 2">
|
||||||
|
<g data-name="Layer 7">
|
||||||
|
<path
|
||||||
|
class="cls-3"
|
||||||
|
d="M221.46,103.71c0,18.83-29.36,18.83-29.12,0C192.1,84.88,221.46,84.88,221.46,103.71Z" />
|
||||||
|
<path
|
||||||
|
class="cls-4"
|
||||||
|
d="M179.9,4.15A175.48,175.48,0,1,0,355.38,179.63,175.48,175.48,0,0,0,179.9,4.15Zm-40.79,264.5c-.23-17.82,27.58-17.82,27.58,0S138.88,286.48,139.11,268.65ZM218.6,168.24A79.65,79.65,0,0,1,205.15,174a12.76,12.76,0,0,0-6.29,4.65L167.54,222a1.36,1.36,0,0,1-2.46-.8v-35.8a2.58,2.58,0,0,0-3.06-2.53c-15.43,3-30.23,7.7-42.73,19.94-38.8,38-29.42,105.69,16.09,133.16a162.25,162.25,0,0,1-91.47-67.27C-3.86,182.26,34.5,47.25,138.37,25.66c46.89-9.75,118.25,5.16,123.73,62.83C265.15,120.64,246.56,152.89,218.6,168.24Z" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
@ -7,102 +8,39 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
id="chapril-logo"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
||||||
class="converse-svg-logo"
|
class="converse-svg-logo"
|
||||||
viewBox="0 0 364 364"
|
xml:space="preserve" version="1.1"
|
||||||
version="1.1"
|
viewBox="0 0 376 311" height="20%" width="6rem"
|
||||||
id="svg13"
|
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
||||||
sodipodi:docname="conversejs-with-byline.svg"
|
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview16" showgrid="false" inkscape:zoom="0.75884244" inkscape:cx="188" inkscape:cy="155.5" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg108"/>
|
||||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
<metadata id="metadata114">
|
||||||
<metadata
|
|
||||||
id="metadata19">
|
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work rdf:about="">
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
<dc:title/>
|
||||||
<dc:title>Converse</dc:title>
|
<cc:license rdf:resource="GFDL version 1.3 ou ultérieure, Creative Commons By Sa version 2.0 ou ultérieure, Licence Art Libre version 1.3 ou ultérieure"/>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Antoine BARDELLI</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs
|
<defs id="defs112"/>
|
||||||
id="defs17">
|
<g id="g250" style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" transform="translate(4.6079614e-7,-4.4571451e-6)">
|
||||||
<inkscape:perspective
|
<path id="path124" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 96.70711,209.69029 c -2.742,1.114 -5.399,1.8 -7.885,2.057 -0.771,0.085 -1.629,0.171 -2.4,0.171 -2.571,0 -5.314,-0.428 -8.228,-1.286 -2.486,-0.857 -4.628,-2.314 -6.514,-4.371 -1.886,-1.8 -3.343,-3.942 -4.371,-6.428 -0.943,-2.486 -1.457,-5.057 -1.543,-7.714 0,-0.086 0,-0.257 0,-0.343 0,-2.485 0.514,-4.971 1.543,-7.371 1.028,-2.314 2.485,-4.457 4.371,-6.428 1.886,-2.057 4.028,-3.514 6.514,-4.371 2.828,-1.029 5.571,-1.543 8.142,-1.543 0.257,0 0.6,0 0.857,0 3.686,0.171 6.857,1.028 9.686,2.657 -0.086,1.457 -0.343,3.514 -0.6,6.343 -2.743,-1.372 -5.4,-2.143 -7.971,-2.315 -1.972,-0.171 -3.943,0.086 -6,0.772 -1.629,0.514 -3.171,1.457 -4.457,2.914 -1.286,1.371 -2.228,2.828 -2.914,4.457 -0.772,1.628 -1.114,3.342 -1.114,5.142 0.085,1.886 0.428,3.6 1.114,5.229 0.686,1.714 1.628,3.171 2.914,4.457 1.286,1.285 2.828,2.228 4.457,2.914 2.486,0.685 4.543,1.028 6.343,1.028 3.342,-0.343 5.999,-1.2 8.056,-2.657 0,1.457 0,3.686 0,6.686 z"/>
|
||||||
sodipodi:type="inkscape:persp3d"
|
<path id="path126" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 137.33411,184.91929 c 0,1.8 0,3.515 0,5.314 0,2.657 0,5.4 -0.086,8.057 0,4.457 0,8.743 0,12.771 -1.714,0 -4.285,0 -7.799,0 0,-3.857 0,-11.571 0.085,-23.142 0.086,-1.114 -0.171,-2.228 -0.6,-3.257 -0.428,-1.028 -1.028,-1.971 -1.8,-2.742 -0.942,-0.943 -2.142,-1.715 -3.599,-2.143 -1.029,-0.257 -2.229,-0.429 -3.6,-0.343 -1.115,0.086 -2.229,0.343 -3.257,0.686 -1.029,0.428 -2.143,1.028 -3.343,1.8 -0.6,0.6 -1.029,1.2 -1.457,1.8 v 27.341 h -8.057 v -62.997 h 8.057 v 29.313 c 0.943,-1.114 1.8,-1.971 2.571,-2.486 1.629,-0.942 3,-1.542 4.2,-1.885 1.971,-0.429 3.771,-0.6 5.571,-0.6 2.143,0.085 3.943,0.343 5.4,0.943 1.543,0.6 2.914,1.542 4.028,2.742 2.4,2.572 3.6,5.486 3.686,8.828 z"/>
|
||||||
inkscape:vp_x="0 : 182 : 1"
|
<path id="path128" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 169.38911,146.86429 c 4.714,10.714 14.228,32.056 28.37,64.111 -1.371,0 -4.114,0 -8.313,0 -1.286,-2.657 -3.772,-7.971 -7.543,-15.942 -4.028,0 -12.085,0 -24.17,0 -1.114,2.657 -3.514,7.971 -7.028,15.942 -1.457,0 -4.2,0 -8.4,0 4.543,-10.713 13.542,-32.055 27.084,-64.111 z m 9.171,40.884 c -1.457,-3.686 -4.542,-10.885 -9.085,-21.77 -1.457,3.599 -4.285,10.885 -8.571,21.77 3,0 8.828,0 17.656,0 z"/>
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
<path id="path130" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 212.15911,199.83329 c 0.257,0.429 0.6,0.771 0.857,1.114 1.114,1.457 2.657,2.743 4.457,3.686 1.457,0.685 3.085,1.028 4.8,1.028 0.085,0 0.257,0 0.428,0 1.8,0.086 3.514,-0.257 5.143,-1.028 1.628,-0.6 3.171,-1.629 4.457,-2.914 1.285,-1.286 2.228,-2.743 2.914,-4.457 0.685,-1.543 1.028,-3.343 1.028,-5.314 0.086,-1.715 -0.257,-3.429 -1.028,-5.057 -0.6,-1.629 -1.629,-3.171 -2.914,-4.457 -1.286,-1.286 -2.743,-2.229 -4.457,-2.914 -1.115,-0.515 -2.572,-0.772 -4.2,-0.772 -0.172,0 -0.343,0 -0.429,0 -2.657,0.086 -4.714,0.429 -5.999,1.2 -3.429,1.8 -5.143,3.857 -5.143,6.171 0,3 0,7.629 0.086,13.714 z m 0,8.314 c -0.086,3.343 -0.086,10.028 -0.086,20.056 -1.286,0 -4.028,0 -8.057,0 v -54.769 c 2.657,0 5.4,0 8.057,0 0.086,1.286 0.086,2.4 0.086,3.257 0.857,-1.114 2.314,-2.142 4.457,-3.085 2.4,-1.029 4.971,-1.543 7.714,-1.543 2.657,0 5.228,0.514 7.628,1.543 2.485,1.028 4.714,2.485 6.514,4.371 1.971,1.886 3.428,4.028 4.371,6.428 1.028,2.486 1.543,4.971 1.543,7.543 0,0.085 0,0.085 0,0.171 0,2.657 -0.515,5.228 -1.543,7.714 -1.029,2.486 -2.486,4.628 -4.371,6.428 -1.886,1.972 -4.029,3.429 -6.514,4.371 -2.486,1.029 -5.057,1.543 -7.628,1.543 -2.657,-0.085 -5.229,-0.6 -7.714,-1.543 -1.372,-0.514 -2.829,-1.371 -4.457,-2.485 z"/>
|
||||||
inkscape:vp_z="364 : 182 : 1"
|
<path id="path132" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 271.04211,180.46329 c 0,-0.086 -0.686,-0.172 -2.143,-0.429 -2.743,-0.086 -5.228,0.857 -7.371,2.828 -0.857,0.772 -1.543,1.886 -2.057,3.343 -0.343,1.371 -0.515,2.829 -0.6,4.457 0,4.543 0,11.399 0,20.399 -1.286,0 -3.943,0 -7.971,0 0,-6.257 0,-18.856 -0.086,-37.627 1.286,0 3.943,0 7.8,0 0.085,0.772 0.085,2.4 0.085,4.8 0,0.514 0,1.543 -0.085,3.171 1.028,-4.199 3.085,-6.856 6.085,-7.971 1.2,-0.343 2.4,-0.514 3.686,-0.514 0.857,-0.086 1.714,0 2.657,0.171 0,1.629 0,4.115 0,7.372 z"/>
|
||||||
inkscape:persp3d-origin="182 : 121.33333 : 1"
|
<path id="path134" style="clip-rule:evenodd;fill:#005184;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 277.47011,163.23529 c 0,-1.372 0.514,-2.4 1.457,-3.171 0.943,-0.772 1.971,-1.2 3.085,-1.2 0.086,0 0.172,0 0.258,0 1.114,0 2.057,0.342 2.999,1.028 0.943,0.771 1.372,1.8 1.372,3.257 0,1.457 -0.429,2.571 -1.372,3.428 -0.942,0.858 -2.057,1.286 -3.257,1.286 -1.114,0 -2.142,-0.428 -3.085,-1.2 -0.943,-0.771 -1.457,-1.971 -1.457,-3.428 z"/>
|
||||||
id="perspective2147" />
|
<path style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" id="path136" d="m 278.15611,173.34929 c 1.371,0 4.028,0 8.056,0 0,6.342 0,18.942 0,37.883 -1.285,0 -3.942,0 -7.885,0 0,-6.256 -0.086,-18.941 -0.171,-37.883 z"/>
|
||||||
</defs>
|
<path id="path138" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 295.55511,146.26429 v 64.797 c 1.285,0 3.942,0 7.885,0 0,-10.799 0,-32.398 0,-64.797 -1.286,0 -3.943,0 -7.885,0 z"/>
|
||||||
<sodipodi:namedview
|
<path id="path144" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 79.90511,45.008286 c 0.902,0.305 0.903,0.306 1.74,0.76 0,0 2.36,1.6 3.541,2.399 16.192,11.056 46.905,36.807 46.905,36.807 l -0.62,0.933 c 0,0 -30.962,-17.269 -47.812,-25.837 -0.154,2.104 -0.309,4.208 -0.465,6.312 l -0.126,1.689 c -0.635,7.993 -1.295,15.97 -2,23.945 l -0.096,1.076 c -0.824,8.583004 -1.124,17.369004 -3.069,25.573004 -1.802,7.601 -10.27,13.477 -15.485,20.114 -10.856,13.816 -19.361,29.582 -21.64,47.603 -2.579,20.389 5.773,40.853 21.062,55.815 5.92,5.794 12.534,10.853 19.323,15.617 l -3.768,5.852 c -20.581,-12.139 -40.182,-28.618 -47.181,-51.981 -6.615,-22.081 -1.608,-47.917 10.144,-67.278 6.689,-11.02 14.834,-21.098 23.413,-30.201 0,0 0.585,-2.521 0.874,-4.27 3.14,-19.016004 5.383,-38.057004 7.92,-57.227004 l 0.274,-2.074 0.28,-1.686 c 0.307,-0.8 0.337,-1.033 0.841,-1.737 1.119,-1.562 2.062,-2.818 5.945,-2.204 z"/>
|
||||||
pagecolor="#ffffff"
|
<path id="path146" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 300.65411,44.858286 c 2.74,0.486 2.778,0.936 3.448,1.587 2.555,2.484 2.191,7.238 2.75,11.471 2.467,18.612 4.172,37.353 7.871,55.935004 0,0 3.607,4.013 6.264,7.099 11.064,12.849 21.019,26.411 26.284,43.975 6.27,20.92 3.585,41.82 -7.633,60.315 -9.349,15.414 -24.268,26.668 -39.964,35.962 l -0.593,-1.853 c 17.279,-12.08 34.133,-26.972 39.318,-48.24 3.677,-15.083 -0.809,-32.515 -6.571,-47.292 -5.094,-13.065 -14.589,-24.209 -23.233,-34.394 -1.789,-2.107 -3.603,-4.192 -5.472,-6.229 0,0 -2.204,-3.069 -2.806,-6.739 -2.582,-15.749 -3.416,-31.540004 -4.699,-47.326004 0,0 -0.34,-4.424 -0.589,-7.754 l -0.098,-1.322 -30.582,18.428 -15.221,9.11 -0.801,0.477 -3.955,-6.151 0.767,-0.531 14.605,-10.067 37.28,-25.534 c 0,0 1.172,-0.939 3.63,-0.927 z m 14.362,70.312004 c 0.178,0.639 -0.049,-0.186 0,0 z"/>
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1434"
|
|
||||||
inkscape:window-height="951"
|
|
||||||
id="namedview15"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="1.8338154"
|
|
||||||
inkscape:cx="225.17086"
|
|
||||||
inkscape:cy="243.79827"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg13" />
|
|
||||||
<title
|
|
||||||
id="title2">Converse</title>
|
|
||||||
<g
|
|
||||||
class="cls-1"
|
|
||||||
id="g904"
|
|
||||||
transform="matrix(0.2441072,0,0,0.2441072,12.20969,55.55023)">
|
|
||||||
<g
|
|
||||||
data-name="Layer 2"
|
|
||||||
id="g10">
|
|
||||||
<g
|
|
||||||
data-name="Layer 7"
|
|
||||||
id="g8">
|
|
||||||
<path
|
|
||||||
class="cls-3"
|
|
||||||
d="m 221.46,103.71 c 0,18.83 -29.36,18.83 -29.12,0 -0.24,-18.83 29.12,-18.83 29.12,0 z"
|
|
||||||
id="path4"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
class="cls-4"
|
|
||||||
d="M 179.9,4.15 C 108.92504,4.15 44.938239,46.904566 17.778836,112.4757 -9.3805118,178.0467 5.6365472,253.52014 55.823205,303.70679 106.00986,353.89345 181.4833,368.91051 247.0543,341.75116 312.62543,314.59176 355.38,250.60496 355.38,179.63 355.38,82.715072 276.81493,4.15 179.9,4.15 Z m -40.79,264.5 c -0.23,-17.82 27.58,-17.82 27.58,0 0,17.82 -27.81,17.83 -27.58,0 z M 218.6,168.24 c -4.29711,2.32859 -8.79944,4.25673 -13.45,5.76 -2.53177,0.85328 -12.23498,3.26952 -13.79313,5.4398 C 180.90809,189.252 165.08,221.2 165.08,221.2 v -35.8 c -0.003,-1.6153 -1.4729,-2.83052 -3.06,-2.53 -15.43,3 -30.23,7.7 -42.73,19.94 -38.8,38 -29.025098,103.71549 16.4849,131.18549 C 98.17801,323.32071 65.725789,295.74404 44.332966,263.03587 -3.4370336,176.59587 35.058475,51.159326 138.92848,29.569326 185.81848,19.819326 256.62,30.82 262.1,88.49 c 3.05,32.15 -15.54,64.4 -43.5,79.75 z"
|
|
||||||
id="path6"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="sssssscsccccccccccccc" />
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.63063431px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.10960984px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="110.04511"
|
|
||||||
y="98.826035"
|
|
||||||
id="text861"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan859"
|
|
||||||
x="110.04511"
|
|
||||||
y="98.826035"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.63063431px;font-family:Baumans;-inkscape-font-specification:'Baumans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:2.10960984px">converse<tspan
|
|
||||||
style="fill:#a2a2a2;fill-opacity:1;stroke-width:2.10960984px"
|
|
||||||
id="tspan867">.js</tspan></tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.49652481px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.77068853px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="124.58434"
|
|
||||||
y="128.44286"
|
|
||||||
id="text865"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan863"
|
|
||||||
x="124.58434"
|
|
||||||
y="128.44286"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.49652481px;font-family:Muli;-inkscape-font-specification:'Muli, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#484848;fill-opacity:1;stroke-width:0.77068853px">messaging freedom</tspan></text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.8 KiB |
@ -1,85 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
version="1.1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="686.18805"
|
|
||||||
height="191.18677"
|
|
||||||
viewBox="0 0 686.19 191.18677"
|
|
||||||
enable-background="new -42.099 -9.411 750 200"
|
|
||||||
xml:space="preserve"
|
|
||||||
id="svg2"
|
|
||||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
|
||||||
sodipodi:docname="pluckeye.svg"><metadata
|
|
||||||
id="metadata30"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1031"
|
|
||||||
id="namedview28"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:zoom="0.44533333"
|
|
||||||
inkscape:cx="-88.043686"
|
|
||||||
inkscape:cy="95.59337"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg2" /><defs
|
|
||||||
id="defs4" /><path
|
|
||||||
d="m 145.61875,146.43082 c 0,6.15645 -5.32372,11.48006 -11.48017,11.48006 -6.48901,0 -11.14739,-5.32361 -11.14739,-11.48006 V 38.952995 c 0,-6.322719 4.65838,-11.147401 11.14739,-11.147401 6.15545,0 11.48017,4.824682 11.48017,11.147401 z"
|
|
||||||
id="path6"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 216.32874,77.884176 c 0,-6.155351 5.32372,-10.981066 11.48006,-10.981066 6.48901,0 10.98111,4.825715 10.98111,10.981066 v 68.546644 c 0,6.15645 -4.4921,11.48006 -10.98111,11.48006 -3.82665,0 -7.48702,-2.1633 -9.48304,-5.15733 -5.82367,3.3277 -12.64435,5.15733 -19.79881,5.15733 -22.79373,0 -41.26086,-18.46802 -41.26086,-41.09447 V 78.384203 c 0,-6.489043 4.99205,-11.48006 11.31378,-11.48006 6.32162,0 11.48006,4.991017 11.48006,11.48006 v 38.432207 c 0,10.14838 8.48503,17.96907 18.46702,17.96907 9.9821,0 17.80179,-7.81969 17.80179,-17.96907 z"
|
|
||||||
id="path8"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 291.86761,157.91088 c -25.62148,0 -45.58657,-20.29776 -45.58657,-45.2549 0,-25.621434 19.96509,-45.75287 45.58657,-45.75287 7.32074,0 14.3087,1.663328 20.9631,4.991017 5.65739,3.162387 7.98608,10.482061 4.82466,15.972083 -3.3277,5.657379 -9.64932,7.986036 -15.30671,4.99205 -3.16142,-1.663328 -7.32074,-2.496025 -10.48205,-2.496025 -12.81074,0 -22.62745,9.650405 -22.62745,22.293745 0,12.1464 9.81571,21.79583 22.62745,21.79583 3.16131,0 7.32063,-0.83173 10.48205,-2.32869 5.65739,-2.66236 11.97901,-0.66634 15.30671,4.65838 3.16142,5.65739 0.83173,12.97813 -4.82466,16.30472 -6.6554,2.99503 -13.64236,4.82466 -20.9631,4.82466 z"
|
|
||||||
id="path10"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 403.67318,141.27349 c 3.3277,5.65739 0.49906,12.1464 -4.65827,15.14043 -5.82378,3.3277 -12.97813,0.83162 -16.13844,-4.82466 l -25.95415,-34.93924 -7.65341,5.32372 v 24.45708 c 0,6.15645 -5.32361,11.48006 -11.48006,11.48006 -6.48801,0 -11.14628,-5.32361 -11.14628,-11.48006 V 38.952995 c 0,-6.322719 4.65827,-11.147401 11.14628,-11.147401 6.15645,0 11.48006,4.824682 11.48006,11.147401 v 54.570594 l 35.93725,-24.124454 c 4.99105,-3.993018 12.97702,-2.32969 16.13844,2.32969 3.82565,5.656346 2.32858,12.977064 -3.16242,16.803747 l -21.96111,14.974088 z"
|
|
||||||
id="path12"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 472.39037,100.84431 c -2.66136,-4.824696 -6.82168,-8.152385 -12.31168,-10.148372 -1.99702,-0.832697 -3.99304,-0.832697 -5.99006,-0.832697 -12.1454,0 -22.46017,10.315729 -22.46017,22.461069 0,12.31179 10.31477,22.29378 22.46017,22.29378 3.16242,0 6.6554,-1.16429 9.98299,-2.66125 5.65739,-2.66236 13.14341,-0.66645 15.80577,4.82466 2.66136,5.32372 0.66534,12.81074 -4.82466,15.4731 -6.6554,3.49398 -14.3087,5.65739 -20.9641,5.65739 -25.12142,0 -45.58657,-20.63043 -45.58657,-45.58657 0,-25.289841 20.46515,-45.421277 45.58657,-45.421277 42.25987,1.496993 44.92123,39.264827 44.92123,45.421277 0,6.15534 -5.82378,11.64534 -11.81273,11.64534 h -32.60955 c -6.48801,0 -11.31367,-5.49 -11.31367,-11.64534 0,-6.82168 4.82566,-11.48008 11.31367,-11.48008 h 17.80279 z"
|
|
||||||
id="path14"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 565.56464,78.384203 c 0,-6.322708 4.99205,-11.48006 11.31378,-11.48006 6.32162,0 11.31367,5.157352 11.31367,11.48006 v 79.527787 c 0,18.63341 -14.97404,33.27478 -33.60745,33.27478 h -7.98608 c -6.15534,0 -11.31267,-5.15733 -11.31267,-11.31267 0,-6.32273 5.15733,-11.14739 11.31267,-11.14739 h 7.98608 c 6.15534,0 10.98,-4.65838 10.98,-10.81472 v -4.15943 c -5.32361,2.66236 -11.31267,4.15943 -17.80168,4.15943 -22.79384,0 -41.26097,-18.46813 -41.26097,-41.09458 V 78.385236 c 0,-6.489043 4.99105,-11.48006 11.31278,-11.48006 6.32273,0 11.48006,4.991017 11.48006,11.48006 v 38.432174 c 0,10.14838 8.48503,17.96907 18.46702,17.96907 9.9831,0 17.80179,-7.81969 17.80179,-17.96907 V 78.384203 Z"
|
|
||||||
id="path16"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 659.56953,100.84431 c -2.66125,-4.824696 -6.82168,-8.152385 -12.31168,-10.148372 -1.99702,-0.832697 -3.99304,-0.832697 -5.99006,-0.832697 -12.1454,0 -22.46006,10.315729 -22.46006,22.461069 0,12.31179 10.31466,22.29378 22.46006,22.29378 3.16242,0 6.6554,-1.16429 9.9831,-2.66125 5.65739,-2.66236 13.14341,-0.66645 15.80577,4.82466 2.66125,5.32372 0.66523,12.81074 -4.82477,15.4731 -6.65529,3.49398 -14.3087,5.65739 -20.9641,5.65739 -25.12142,0 -45.58657,-20.63043 -45.58657,-45.58657 0,-25.289841 20.46515,-45.421277 45.58657,-45.421277 42.25987,1.496993 44.92123,39.264827 44.92123,45.421277 0,6.15534 -5.82367,11.64534 -11.81273,11.64534 h -32.60955 c -6.48801,0 -11.31367,-5.49 -11.31367,-11.64534 0,-6.82168 4.82566,-11.48008 11.31367,-11.48008 h 17.80279 z"
|
|
||||||
id="path18"
|
|
||||||
inkscape:connector-curvature="0" /><path
|
|
||||||
d="m 93.208361,28.712678 -8.942715,23.461191 c 6.562397,8.976804 8.571824,20.980672 4.29883,32.190053 C 81.853305,101.97041 62.279755,110.7406 44.673268,104.02839 27.064715,97.318249 18.295567,77.742633 25.008803,60.135113 29.54008,48.246974 39.937435,40.398338 51.631359,38.612061 L 60.408772,15.588917 C 36.22336,14.027866 12.839646,28.187848 3.7502295,52.031304 -7.4219625,81.343067 7.2597485,114.11162 36.568415,125.28589 65.878123,136.45916 98.649821,121.77747 109.82302,92.467742 118.68414,69.22247 111.27043,43.811777 93.208361,28.712678 Z"
|
|
||||||
id="path20"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="fill:#09476b" /><path
|
|
||||||
d="m 84.968174,0.77392782 v 0 C 78.879979,-1.5464678 72.013835,1.5291407 69.691377,7.617335 L 57.064543,40.741327 c -2.322458,6.089228 0.75419,12.955371 6.843418,15.275764 v 0 c 6.087162,2.322459 12.954327,-0.753145 15.275753,-6.843406 L 91.810547,16.04969 C 94.131973,9.9614914 91.056369,3.0963883 84.968174,0.77392782 Z"
|
|
||||||
id="path22"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="fill:#09476b" /><path
|
|
||||||
d="m 22.705971,110.04938 c 0.004,-0.12185 0.01855,-0.2407 0.01855,-0.36466 V 74.236213 c 0,-6.515901 -5.106724,-11.835456 -11.361253,-11.835456 -6.2555605,0 -11.3622875,5.319555 -11.3622875,11.835456 v 35.449507 c 0,0.15195 0.01756,0.30068 0.02169,0.45152 -0.0021,0.12196 -0.01756,0.2407 -0.01756,0.36366 v 35.44952 c 0,6.51489 5.106728,11.8355 11.3612555,11.8355 6.254528,0 11.362285,-5.32061 11.362285,-11.8355 V 110.5009 c 0,-0.15295 -0.01655,-0.30068 -0.02277,-0.45152 z"
|
|
||||||
id="path24"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="fill:#09476b" /><path
|
|
||||||
d="m 52.570466,85.838189 v 0 c -6.11196,-2.32969 -9.17414,-9.165865 -6.843406,-15.27783 v 0 c 2.32969,-6.109888 9.164832,-9.173096 15.275753,-6.843406 v 0 c 6.111954,2.329689 9.175171,9.16381 6.844439,15.276797 v 0 c -2.32969,6.111954 -9.164832,9.174129 -15.276786,6.844439 z"
|
|
||||||
id="path26"
|
|
||||||
inkscape:connector-curvature="0" /></svg>
|
|
Before Width: | Height: | Size: 8.2 KiB |
@ -2,7 +2,7 @@
|
|||||||
"short_name": "Converse",
|
"short_name": "Converse",
|
||||||
"name": "Converse Chat",
|
"name": "Converse Chat",
|
||||||
"description": "Messaging Freedom",
|
"description": "Messaging Freedom",
|
||||||
"version": "8.0.1",
|
"version": "10.1.6",
|
||||||
"categories": ["social"],
|
"categories": ["social"],
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<title>Converse.js | Mobile</title>
|
<title>Converse.js | Mobile</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Converse: An XMPP chat client which can be integrated into any website" />
|
<meta name="description" content="Converse: An XMPP chat client which can be integrated into any website" />
|
||||||
<meta name="author" content="JC Brand" />
|
<meta name="author" content="JC Brand" />
|
||||||
@ -18,9 +19,9 @@
|
|||||||
<script type="text/javascript" src="analytics.js"></script>
|
<script type="text/javascript" src="analytics.js"></script>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/8.0.1/dist/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css" />
|
||||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
<script src="https://cdn.conversejs.org/8.0.1/dist/converse.min.js"></script>
|
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="page-top" data-spy="scroll" class="converse-website">
|
<body id="page-top" data-spy="scroll" class="converse-website">
|
||||||
@ -240,11 +241,9 @@
|
|||||||
<h2>Converse is supported by:</h2>
|
<h2>Converse is supported by:</h2>
|
||||||
<ul >
|
<ul >
|
||||||
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
||||||
<li><a href="https://wikisuite.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 4em" src="/logo/wikisuite-white.png" alt="WikiSuite"></a></li>
|
|
||||||
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
||||||
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
|
|
||||||
<li><a href="https://www.b1-systems.de?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 5em" src="/logo/b1-systems.svg" alt="B1 Systems"></a></li>
|
|
||||||
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
||||||
|
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
38892
package-lock.json
generated
130
package.json
@ -1,42 +1,46 @@
|
|||||||
{
|
{
|
||||||
"name": "converse.js",
|
"name": "converse.js",
|
||||||
"version": "8.0.1",
|
"version": "10.1.6",
|
||||||
"description": "Browser based XMPP chat client",
|
"description": "Browser based XMPP chat client",
|
||||||
"browser": "dist/converse.js",
|
"browser": "dist/converse.js",
|
||||||
"module": "src/converse.js",
|
"module": "src/index.js",
|
||||||
|
"workspaces": [
|
||||||
|
"src/headless"
|
||||||
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"*.js",
|
|
||||||
"*.json",
|
|
||||||
"CHANGES.md",
|
"CHANGES.md",
|
||||||
"LICENSE.txt",
|
"LICENSE.txt",
|
||||||
"README.md",
|
"README.md",
|
||||||
"COPYRIGHT",
|
"COPYRIGHT",
|
||||||
"dist/",
|
"dist/",
|
||||||
"docs/**/*.rst",
|
|
||||||
"docs/**/*.md",
|
"docs/**/*.md",
|
||||||
|
"docs/**/*.rst",
|
||||||
"sass/**/*.scss",
|
"sass/**/*.scss",
|
||||||
"src/*.js",
|
|
||||||
"src/**/*.pot",
|
|
||||||
"src/**/*.po",
|
|
||||||
"src/**/*.js",
|
|
||||||
"src/**/*.html",
|
"src/**/*.html",
|
||||||
"src/**/*.svg",
|
"src/**/*.js",
|
||||||
|
"src/**/*.json",
|
||||||
"src/**/*.md",
|
"src/**/*.md",
|
||||||
|
"src/**/*.po",
|
||||||
|
"src/**/*.pot",
|
||||||
|
"src/**/*.scss",
|
||||||
|
"src/**/*.svg",
|
||||||
"src/**/*.txt",
|
"src/**/*.txt",
|
||||||
"src/**/*.json"
|
"3rdparty/*.js"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "webpack serve --config webpack.serve.js",
|
"build": "webpack --config webpack/webpack.build.js",
|
||||||
|
"lint": "eslint src/**/*.js",
|
||||||
|
"test": "karma start karma.conf",
|
||||||
|
"cdn": "ASSET_PATH=https://cdn.conversejs.org/dist/ npm run build",
|
||||||
"clean": "rm -rf node_modules dist *.zip src/headless/dist src/headless/node_modules",
|
"clean": "rm -rf node_modules dist *.zip src/headless/dist src/headless/node_modules",
|
||||||
"headless": "webpack --config webpack.headless.js",
|
"dev": "webpack --config webpack/webpack.build.js --mode=development",
|
||||||
"nodeps": "webpack --config webpack.nodeps.js",
|
"headless": "webpack --config webpack/webpack.headless.js",
|
||||||
"cdn": "ASSET_PATH=https://cdn.conversejs.org/dist/ npm run dev && ASSET_PATH=https://cdn.conversejs.org/dist/ npm run build",
|
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
|
||||||
"prod": "webpack --config webpack.prod.js",
|
"nodeps": "webpack --config webpack/webpack.nodeps.js",
|
||||||
"build": "npm run dev && npm run prod",
|
"serve": "webpack serve --config webpack/webpack.serve.js",
|
||||||
"dev": "webpack --config webpack.dev.js",
|
"watch": "webpack --watch --config webpack/webpack.build.js --mode=development",
|
||||||
"watch": "webpack --watch --config webpack.dev.js",
|
"types": "tsc --declaration --emitDeclarationOnly --allowJs",
|
||||||
"lerna": "lerna bootstrap --hoist --ignore-scripts",
|
"check:types": "tsc --noEmit"
|
||||||
"prepare": "npm run lerna && npm run build"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -63,56 +67,70 @@
|
|||||||
"browser": "*"
|
"browser": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.14.5",
|
"@babel/cli": "^7.17.10",
|
||||||
"@babel/core": "^7.14.6",
|
"@babel/core": "^7.18.5",
|
||||||
"@babel/preset-env": "^7.14.7",
|
"@babel/preset-env": "^7.18.2",
|
||||||
"@converse/headless": "file:src/headless",
|
"@converse/headless": "file:src/headless",
|
||||||
"autoprefixer": "^9.8.6",
|
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"autoprefixer": "^10.4.5",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^9.1.0",
|
||||||
"bootstrap.native-loader": "2.0.0",
|
"bootstrap.native-loader": "2.0.0",
|
||||||
"clean-css-cli": "^5.3.0",
|
"clean-css-cli": "^5.6.2",
|
||||||
"copy-webpack-plugin": "^9.0.1",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^5.2.7",
|
"css-loader": "^6.7.1",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^8.21.0",
|
||||||
"fast-text-encoding": "^1.0.3",
|
"fast-text-encoding": "^1.0.3",
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"http-server": "^0.12.3",
|
"http-server": "^14.1.0",
|
||||||
"imports-loader": "^3.0.0",
|
"imports-loader": "^4.0.0",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jsdoc": "^3.6.7",
|
"jsdoc": "^4.0.0",
|
||||||
"karma": "^6.3.4",
|
"karma": "^6.3.19",
|
||||||
"karma-chrome-launcher": "^3.1.0",
|
"karma-chrome-launcher": "^3.1.1",
|
||||||
"karma-cli": "^2.0.0",
|
"karma-cli": "^2.0.0",
|
||||||
"karma-jasmine": "^4.0.1",
|
"karma-jasmine": "^5.0.0",
|
||||||
"karma-jasmine-html-reporter": "^1.7.0",
|
"karma-jasmine-html-reporter": "^2.0.0",
|
||||||
"karma-webpack": "^5.0.0",
|
"karma-webpack": "^5.0.0",
|
||||||
"lerna": "^4.0.0",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"mini-css-extract-plugin": "^1.6.2",
|
"minimist": "^1.2.6",
|
||||||
"minimist": "^1.2.3",
|
"po-loader": "0.7.0",
|
||||||
"npm": "^7.19.1",
|
"po2json": "^1.0.0-beta-3",
|
||||||
"po-loader": "^0.5.0",
|
"postcss": "^8.4.16",
|
||||||
"po2json": "^1.0.0-beta",
|
"postcss-loader": "^7.0.1",
|
||||||
"postcss-clean": "^1.2.2",
|
"prettierx": "^0.19.0",
|
||||||
"postcss-loader": "^6.1.1",
|
"sass": "^1.51.0",
|
||||||
"prettierx": "^0.18.2",
|
"sass-loader": "^13.1.0",
|
||||||
"run-headless-chromium": "^0.1.1",
|
|
||||||
"sass": "^1.35.2",
|
|
||||||
"sass-loader": "^11.0.1",
|
|
||||||
"style-loader": "^3.1.0",
|
"style-loader": "^3.1.0",
|
||||||
"webpack": "^5.36.1",
|
"tsc": "^2.0.4",
|
||||||
"webpack-cli": "^4.7.2",
|
"typescript": "^4.9.5",
|
||||||
"webpack-dev-server": "^4.0.0",
|
"typescript-eslint-parser": "^22.0.0",
|
||||||
|
"uglify-js": "^3.17.4",
|
||||||
|
"webpack": "^5.86.0",
|
||||||
|
"webpack-cli": "^5.1.4",
|
||||||
|
"webpack-dev-server": "^4.8.1",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "5.14.0",
|
"@converse/openpromise": "^0.0.1",
|
||||||
|
"@converse/skeletor": "^0.0.8",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"bootstrap.native": "^2.0.27",
|
"bootstrap.native": "^2.0.27",
|
||||||
|
"client-compress": "^2.2.2",
|
||||||
|
"dayjs": "^1.11.8",
|
||||||
"dompurify": "^2.3.1",
|
"dompurify": "^2.3.1",
|
||||||
"favico.js-slevomat": "^0.3.11",
|
"favico.js-slevomat": "^0.3.11",
|
||||||
|
"gifuct-js": "^2.1.2",
|
||||||
"jed": "1.1.1",
|
"jed": "1.1.1",
|
||||||
"lit": "^2.0.0-rc.2"
|
"lit": "^2.4.0",
|
||||||
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"pluggable.js": "^3.0.1",
|
||||||
|
"sizzle": "^2.3.5",
|
||||||
|
"sprintf-js": "^1.1.2",
|
||||||
|
"strophe.js": "^1.6.2",
|
||||||
|
"urijs": "^1.19.10"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"autoprefixer": "10.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
require('autoprefixer'),
|
require('autoprefixer'),
|
||||||
require('postcss-clean')
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1 +1,3 @@
|
|||||||
zc.buildout==2.13.2
|
Sphinx==4.5.0
|
||||||
|
docutils==0.17.1
|
||||||
|
sphinx-bootstrap-theme==0.8.1
|
||||||
|
@ -28,7 +28,7 @@ const converse = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
initialize (settings={}) {
|
initialize (settings={}) {
|
||||||
converse.load(settings).initialize(settings);
|
return converse.load(settings).initialize(settings);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +49,7 @@ const converse = {
|
|||||||
*
|
*
|
||||||
* @memberOf converse
|
* @memberOf converse
|
||||||
* @method load
|
* @method load
|
||||||
* @param {object} settings A map of configuration-settings that are needed at load time.
|
* @param { object } settings A map of configuration-settings that are needed at load time.
|
||||||
* @example
|
* @example
|
||||||
* converse.load({assets_path: '/path/to/assets/'});
|
* converse.load({assets_path: '/path/to/assets/'});
|
||||||
*/
|
*/
|
||||||
@ -57,7 +57,7 @@ const converse = {
|
|||||||
if (settings.assets_path) {
|
if (settings.assets_path) {
|
||||||
__webpack_public_path__ = settings.assets_path; // eslint-disable-line no-undef
|
__webpack_public_path__ = settings.assets_path; // eslint-disable-line no-undef
|
||||||
}
|
}
|
||||||
require('./converse.js');
|
require('./index.js');
|
||||||
Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name]));
|
Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name]));
|
||||||
return converse;
|
return converse;
|
||||||
}
|
}
|
||||||
|
2
src/headless/.npmignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
tests
|
@ -13,5 +13,5 @@ It's also installable with NPM/Yarn as [@converse/headless](https://www.npmjs.co
|
|||||||
|
|
||||||
The main distribution of Converse relies on the headless build.
|
The main distribution of Converse relies on the headless build.
|
||||||
|
|
||||||
The file [src/headless/headless.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/headless.js)
|
The file [src/headless/index.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/index.js)
|
||||||
is used to determine which plugins are included in the build.
|
is used to determine which plugins are included in the build.
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
import log from '@converse/headless/log.js';
|
|
||||||
import { getAppSetting, extendAppSettings, updateAppSettings } from '@converse/headless/shared/settings';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This grouping allows access to the
|
|
||||||
* [configuration settings](/docs/html/configuration.html#configuration-settings)
|
|
||||||
* of Converse.
|
|
||||||
*
|
|
||||||
* @namespace _converse.api.settings
|
|
||||||
* @memberOf _converse.api
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Allows new configuration settings to be specified, or new default values for
|
|
||||||
* existing configuration settings to be specified.
|
|
||||||
*
|
|
||||||
* Note, calling this method *after* converse.initialize has been
|
|
||||||
* called will *not* change the initialization settings provided via
|
|
||||||
* `converse.initialize`.
|
|
||||||
*
|
|
||||||
* @method _converse.api.settings.extend
|
|
||||||
* @param {object} settings The configuration settings
|
|
||||||
* @example
|
|
||||||
* _converse.api.settings.extend({
|
|
||||||
* 'enable_foo': true
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* // The user can then override the default value of the configuration setting when
|
|
||||||
* // calling `converse.initialize`.
|
|
||||||
* converse.initialize({
|
|
||||||
* 'enable_foo': false
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
extend (settings) {
|
|
||||||
return extendAppSettings(settings);
|
|
||||||
},
|
|
||||||
|
|
||||||
update (settings) {
|
|
||||||
log.warn(
|
|
||||||
'The api.settings.update method has been deprecated and will be removed. ' +
|
|
||||||
'Please use api.settings.extend instead.'
|
|
||||||
);
|
|
||||||
return this.extend(settings);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @method _converse.api.settings.get
|
|
||||||
* @returns {*} Value of the particular configuration setting.
|
|
||||||
* @example _converse.api.settings.get("play_sounds");
|
|
||||||
*/
|
|
||||||
get (key) {
|
|
||||||
return getAppSetting(key);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set one or many configuration settings.
|
|
||||||
*
|
|
||||||
* Note, this is not an alternative to calling {@link converse.initialize}, which still needs
|
|
||||||
* to be called. Generally, you'd use this method after Converse is already
|
|
||||||
* running and you want to change the configuration on-the-fly.
|
|
||||||
*
|
|
||||||
* @method _converse.api.settings.set
|
|
||||||
* @param {Object} [settings] An object containing configuration settings.
|
|
||||||
* @param {string} [key] Alternatively to passing in an object, you can pass in a key and a value.
|
|
||||||
* @param {string} [value]
|
|
||||||
* @example _converse.api.settings.set("play_sounds", true);
|
|
||||||
* @example
|
|
||||||
* _converse.api.settings.set({
|
|
||||||
* "play_sounds": true,
|
|
||||||
* "hide_offline_users": true
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
set (key, val) {
|
|
||||||
updateAppSettings(key, val);
|
|
||||||
},
|
|
||||||
};
|
|
@ -2,15 +2,14 @@
|
|||||||
* --------------------
|
* --------------------
|
||||||
* Any of the following components may be removed if they're not needed.
|
* Any of the following components may be removed if they're not needed.
|
||||||
*/
|
*/
|
||||||
import "./plugins/adhoc.js"; // XEP-0050 Ad Hoc Commands
|
|
||||||
import "./plugins/bookmarks/index.js"; // XEP-0199 XMPP Ping
|
import "./plugins/bookmarks/index.js"; // XEP-0199 XMPP Ping
|
||||||
import "./plugins/bosh.js"; // XEP-0206 BOSH
|
import "./plugins/bosh.js"; // XEP-0206 BOSH
|
||||||
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
|
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
|
||||||
import "./plugins/carbons.js"; // XEP-0280 Message Carbons
|
|
||||||
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
|
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
|
||||||
import "./plugins/chatboxes/index.js";
|
import "./plugins/chatboxes/index.js";
|
||||||
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
|
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
|
||||||
import "./plugins/headlines.js"; // Support for headline messages
|
import "./plugins/adhoc/index.js"; // XEP-0050 Ad Hoc Commands
|
||||||
|
import "./plugins/headlines/index.js"; // Support for headline messages
|
||||||
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
|
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
|
||||||
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat
|
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat
|
||||||
import "./plugins/ping/index.js"; // XEP-0199 XMPP Ping
|
import "./plugins/ping/index.js"; // XEP-0199 XMPP Ping
|
||||||
@ -18,7 +17,7 @@ import "./plugins/pubsub.js"; // XEP-0060 Pubsub
|
|||||||
import "./plugins/roster/index.js"; // RFC-6121 Contacts Roster
|
import "./plugins/roster/index.js"; // RFC-6121 Contacts Roster
|
||||||
import "./plugins/smacks/index.js"; // XEP-0198 Stream Management
|
import "./plugins/smacks/index.js"; // XEP-0198 Stream Management
|
||||||
import "./plugins/status/index.js";
|
import "./plugins/status/index.js";
|
||||||
import "./plugins/vcard.js"; // XEP-0054 VCard-temp
|
import "./plugins/vcard/index.js"; // XEP-0054 VCard-temp
|
||||||
/* END: Removable components */
|
/* END: Removable components */
|
||||||
|
|
||||||
import { converse } from "./core.js";
|
import { converse } from "./core.js";
|
@ -1,4 +1,4 @@
|
|||||||
import isElement from 'lodash-es/isElement';
|
import { isElement } from './utils/core.js';
|
||||||
|
|
||||||
const LEVELS = {
|
const LEVELS = {
|
||||||
'debug': 0,
|
'debug': 0,
|
||||||
@ -8,24 +8,26 @@ const LEVELS = {
|
|||||||
'fatal': 4
|
'fatal': 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
const logger = Object.assign({
|
const logger = Object.assign({
|
||||||
'debug': console?.log ? console.log.bind(console) : function noop () {},
|
'debug': console?.log ? console.log.bind(console) : function noop () {},
|
||||||
'error': console?.log ? console.log.bind(console) : function noop () {},
|
'error': console?.log ? console.log.bind(console) : function noop () {},
|
||||||
'info': console?.log ? console.log.bind(console) : function noop () {},
|
'info': console?.log ? console.log.bind(console) : function noop () {},
|
||||||
'warn': console?.log ? console.log.bind(console) : function noop () {}
|
'warn': console?.log ? console.log.bind(console) : function noop () {}
|
||||||
}, console);
|
}, console);
|
||||||
|
/* eslint-enable @typescript-eslint/no-empty-function */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The log namespace
|
* The log namespace
|
||||||
* @namespace log
|
* @namespace log
|
||||||
*/
|
*/
|
||||||
const log = {
|
export default {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The the log-level, which determines how verbose the logging is.
|
* The the log-level, which determines how verbose the logging is.
|
||||||
* @method log#setLogLevel
|
* @method log#setLogLevel
|
||||||
* @param { integer } level - The loglevel which allows for filtering of log messages
|
* @param { number } level - The loglevel which allows for filtering of log messages
|
||||||
*/
|
*/
|
||||||
setLogLevel (level) {
|
setLogLevel (level) {
|
||||||
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
|
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
|
||||||
@ -41,8 +43,8 @@ const log = {
|
|||||||
* When using the 'error' or 'warn' loglevels, a full stacktrace will be
|
* When using the 'error' or 'warn' loglevels, a full stacktrace will be
|
||||||
* logged as well.
|
* logged as well.
|
||||||
* @method log#log
|
* @method log#log
|
||||||
* @param { string } message - The message to be logged
|
* @param { string | Error } message - The message to be logged
|
||||||
* @param { integer } level - The loglevel which allows for filtering of log messages
|
* @param { number } level - The loglevel which allows for filtering of log messages
|
||||||
*/
|
*/
|
||||||
log (message, level, style='') {
|
log (message, level, style='') {
|
||||||
if (LEVELS[level] < LEVELS[this.loglevel]) {
|
if (LEVELS[level] < LEVELS[this.loglevel]) {
|
||||||
@ -93,5 +95,3 @@ const log = {
|
|||||||
this.log(message, 'fatal', style);
|
this.log(message, 'fatal', style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default log;
|
|
||||||
|
713
src/headless/package-lock.json
generated
@ -1,77 +1,656 @@
|
|||||||
{
|
{
|
||||||
"name": "@converse/headless",
|
"name": "@converse/headless",
|
||||||
"version": "8.0.0",
|
"version": "10.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@converse/headless",
|
||||||
|
"version": "10.1.0",
|
||||||
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@converse/openpromise": "^0.0.1",
|
||||||
|
"@converse/skeletor": "0.0.8",
|
||||||
|
"dayjs": "1.11.8",
|
||||||
|
"dompurify": "^2.3.1",
|
||||||
|
"filesize": "^7.0.0",
|
||||||
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"pluggable.js": "3.0.1",
|
||||||
|
"sizzle": "^2.3.5",
|
||||||
|
"sprintf-js": "^1.1.2",
|
||||||
|
"strophe.js": "1.6.0",
|
||||||
|
"urijs": "^1.19.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@converse/localforage-getitems": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"localforage": ">=1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@converse/openpromise": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@converse/skeletor": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@converse/localforage-getitems": "1.4.3",
|
||||||
|
"lit-html": "^2.0.0-rc.2",
|
||||||
|
"localforage": "^1.10.0",
|
||||||
|
"localforage-driver-memory": "^1.0.5",
|
||||||
|
"localforage-setitems": "^1.4.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mergebounce": "0.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
||||||
|
},
|
||||||
|
"node_modules/@xmldom/xmldom": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ==",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/abab": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||||
|
},
|
||||||
|
"node_modules/anymatch": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
|
"dependencies": {
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"picomatch": "^2.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/babel-runtime": {
|
||||||
|
"version": "6.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||||
|
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^2.4.0",
|
||||||
|
"regenerator-runtime": "^0.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/binary-extensions": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"anymatch": "~3.1.2",
|
||||||
|
"braces": "~3.0.2",
|
||||||
|
"glob-parent": "~5.1.2",
|
||||||
|
"is-binary-path": "~2.1.0",
|
||||||
|
"is-glob": "~4.0.1",
|
||||||
|
"normalize-path": "~3.0.0",
|
||||||
|
"readdirp": "~3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.10.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/core-js": {
|
||||||
|
"version": "2.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||||
|
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||||
|
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||||
|
"hasInstallScript": true
|
||||||
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
|
||||||
|
"integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
|
||||||
|
},
|
||||||
|
"node_modules/debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "2.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
||||||
|
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
||||||
|
},
|
||||||
|
"node_modules/filesize": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-Wsstw+O1lZ9gVmOI1thyeQvODsaoId2qw14lCqIzUhoHKXX7T2hVpB7BR6SvgodMBgWccrx/y2eyV8L7tDmY6A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-binary-path": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
|
"dependencies": {
|
||||||
|
"binary-extensions": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/karma-rollup-preprocessor": {
|
||||||
|
"version": "7.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/karma-rollup-preprocessor/-/karma-rollup-preprocessor-7.0.8.tgz",
|
||||||
|
"integrity": "sha512-WiuBCS9qsatJuR17dghiTARBZ7LF+ml+eb7qJXhw7IbsdY0lTWELDRQC/93J9i6636CsAXVBL3VJF4WtaFLZzA==",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^3.3.1",
|
||||||
|
"debounce": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": ">= 1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-html": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/localforage": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/localforage-driver-commons": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage-driver-commons/-/localforage-driver-commons-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-K9PiNNXcyX98lQVyCADjv+QKxFD71y0DtVUhqMjwCkFY/d/g7GdJLPN9U92M7RUvfkL8mzPhC+mWEKo9tur5oQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"tslib": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/localforage-driver-memory": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage-driver-memory/-/localforage-driver-memory-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-m4v478ixdT3hA7gKv+pAxDIWgMKiUV2GuYem5jnpOBQFVJbrHU7jmNlrj8a0MfD9qff3i48E3Yfip5Eu1AN6Qg==",
|
||||||
|
"dependencies": {
|
||||||
|
"localforage-driver-commons": "^1.0.1",
|
||||||
|
"tslib": "^1.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"localforage": "^1.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/localforage-setitems": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-uU2ZydBZZkwaP0s1m5NMJXKOQJ0pfqFjDfHdv/SU9E1SWZpr4U3qpyJLZSZJvCvaAeBEL9Bvzd0/CWC9Jtk3wg==",
|
||||||
|
"dependencies": {
|
||||||
|
"localforage": ">=1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/localforage-webextensionstorage-driver": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage-webextensionstorage-driver/-/localforage-webextensionstorage-driver-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-8DUdLnTiX0aBlKqEMPSKiwesx+nHjdH7Bi8+8Hlq0dKXurW9zJvhVfEDU9Pv/LOskNWGo/PuSzLnAcmFqKCvpg==",
|
||||||
|
"dependencies": {
|
||||||
|
"babel-runtime": "^6.22.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"localforage": "^1.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash-es": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
|
},
|
||||||
|
"node_modules/mergebounce": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mergebounce/-/mergebounce-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-UvBRi+87aFdjLXAQhX3aTja4l5Uc4ZTrSL3V939lR7n7PWrBN8CiQLTprmtVca65Vwu2JuXPbMIvNM4JElMxdQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@converse/openpromise": "0.0.1",
|
||||||
|
"lodash-es": "^4.17.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pluggable.js": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-DQC51A6aKLk6anvyvQfukNcVzGHOI5B04DerHioqLSF7ptI+Nla2hHzG4PGxq8tKqOGwQHnXnj9qxcFM3VViEQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash-es": "^4.17.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
|
"dependencies": {
|
||||||
|
"picomatch": "^2.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||||
|
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "3.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
||||||
|
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sizzle": {
|
||||||
|
"version": "2.3.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
||||||
|
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
||||||
|
},
|
||||||
|
"node_modules/sprintf-js": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||||
|
},
|
||||||
|
"node_modules/strophe.js": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"abab": "^2.0.3",
|
||||||
|
"karma-rollup-preprocessor": "^7.0.8"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@xmldom/xmldom": "0.8.3",
|
||||||
|
"ws": "^8.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
},
|
||||||
|
"node_modules/urijs": {
|
||||||
|
"version": "1.19.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
|
||||||
|
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.12.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||||
|
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@converse/localforage-getitems": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
|
||||||
|
"requires": {
|
||||||
|
"localforage": ">=1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@converse/openpromise": {
|
"@converse/openpromise": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||||
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||||
},
|
},
|
||||||
"@converse/skeletor": {
|
"@converse/skeletor": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
||||||
"integrity": "sha512-x9aQTCwQOao8jdgfb/+EzITB5/sj+WhCu2yp+uccURte3zTZbG6TSJEI2dqpLxThpgmSQwrsRQDDQ3jc7FZ/2Q==",
|
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@converse/localforage-getitems": "1.4.3",
|
||||||
"lit-html": "^2.0.0-rc.2",
|
"lit-html": "^2.0.0-rc.2",
|
||||||
|
"localforage": "^1.10.0",
|
||||||
|
"localforage-driver-memory": "^1.0.5",
|
||||||
|
"localforage-setitems": "^1.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mergebounce": "0.1.0"
|
"mergebounce": "0.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/trusted-types": {
|
"@types/trusted-types": {
|
||||||
"version": "1.0.6",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
||||||
"integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw=="
|
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
||||||
|
},
|
||||||
|
"@xmldom/xmldom": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ==",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"abab": {
|
"abab": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||||
|
},
|
||||||
|
"anymatch": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
|
"requires": {
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"picomatch": "^2.0.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"babel-runtime": {
|
"babel-runtime": {
|
||||||
"version": "6.26.0",
|
"version": "6.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-js": "^2.4.0",
|
"core-js": "^2.4.0",
|
||||||
"regenerator-runtime": "^0.11.0"
|
"regenerator-runtime": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"binary-extensions": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||||
|
},
|
||||||
|
"braces": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||||
|
"requires": {
|
||||||
|
"fill-range": "^7.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chokidar": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||||
|
"requires": {
|
||||||
|
"anymatch": "~3.1.2",
|
||||||
|
"braces": "~3.0.2",
|
||||||
|
"fsevents": "~2.3.2",
|
||||||
|
"glob-parent": "~5.1.2",
|
||||||
|
"is-binary-path": "~2.1.0",
|
||||||
|
"is-glob": "~4.0.1",
|
||||||
|
"normalize-path": "~3.0.0",
|
||||||
|
"readdirp": "~3.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "2.6.12",
|
"version": "2.6.12",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
||||||
},
|
},
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.10.6",
|
"version": "1.11.6",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
|
||||||
"integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw=="
|
"integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
|
||||||
|
},
|
||||||
|
"debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
|
},
|
||||||
|
"dompurify": {
|
||||||
|
"version": "2.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
||||||
|
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
||||||
},
|
},
|
||||||
"filesize": {
|
"filesize": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-7.0.0.tgz",
|
||||||
"integrity": "sha512-Wsstw+O1lZ9gVmOI1thyeQvODsaoId2qw14lCqIzUhoHKXX7T2hVpB7BR6SvgodMBgWccrx/y2eyV8L7tDmY6A=="
|
"integrity": "sha512-Wsstw+O1lZ9gVmOI1thyeQvODsaoId2qw14lCqIzUhoHKXX7T2hVpB7BR6SvgodMBgWccrx/y2eyV8L7tDmY6A=="
|
||||||
},
|
},
|
||||||
|
"fill-range": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||||
|
"requires": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"requires": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"immediate": {
|
"immediate": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
|
},
|
||||||
|
"is-binary-path": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
|
"requires": {
|
||||||
|
"binary-extensions": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
|
||||||
|
},
|
||||||
|
"is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"requires": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||||
|
},
|
||||||
|
"karma-rollup-preprocessor": {
|
||||||
|
"version": "7.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/karma-rollup-preprocessor/-/karma-rollup-preprocessor-7.0.8.tgz",
|
||||||
|
"integrity": "sha512-WiuBCS9qsatJuR17dghiTARBZ7LF+ml+eb7qJXhw7IbsdY0lTWELDRQC/93J9i6636CsAXVBL3VJF4WtaFLZzA==",
|
||||||
|
"requires": {
|
||||||
|
"chokidar": "^3.3.1",
|
||||||
|
"debounce": "^1.2.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"lie": {
|
"lie": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||||
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
|
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"immediate": "~3.0.5"
|
"immediate": "~3.0.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lit-html": {
|
"lit-html": {
|
||||||
"version": "2.0.0-rc.3",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.0-rc.3.tgz",
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
||||||
"integrity": "sha512-Y6P8LlAyQuqvzq6l/Nc4z5/P5M/rVLYKQIRxcNwSuGajK0g4kbcBFQqZmgvqKG+ak+dHZjfm2HUw9TF5N/pkCw==",
|
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/trusted-types": "^1.0.1"
|
"@types/trusted-types": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"localforage": {
|
"localforage": {
|
||||||
@ -85,7 +664,8 @@
|
|||||||
"localforage-driver-commons": {
|
"localforage-driver-commons": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/localforage-driver-commons/-/localforage-driver-commons-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/localforage-driver-commons/-/localforage-driver-commons-1.0.3.tgz",
|
||||||
"integrity": "sha512-K9PiNNXcyX98lQVyCADjv+QKxFD71y0DtVUhqMjwCkFY/d/g7GdJLPN9U92M7RUvfkL8mzPhC+mWEKo9tur5oQ=="
|
"integrity": "sha512-K9PiNNXcyX98lQVyCADjv+QKxFD71y0DtVUhqMjwCkFY/d/g7GdJLPN9U92M7RUvfkL8mzPhC+mWEKo9tur5oQ==",
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"localforage-driver-memory": {
|
"localforage-driver-memory": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
@ -99,15 +679,15 @@
|
|||||||
"localforage-setitems": {
|
"localforage-setitems": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||||
"integrity": "sha1-NrhZDVB9+1yAQDPih+zljYiZbV8=",
|
"integrity": "sha512-uU2ZydBZZkwaP0s1m5NMJXKOQJ0pfqFjDfHdv/SU9E1SWZpr4U3qpyJLZSZJvCvaAeBEL9Bvzd0/CWC9Jtk3wg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"localforage": ">=1.4.0"
|
"localforage": ">=1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"localforage-webextensionstorage-driver": {
|
"localforage-webextensionstorage-driver": {
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage-webextensionstorage-driver/-/localforage-webextensionstorage-driver-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/localforage-webextensionstorage-driver/-/localforage-webextensionstorage-driver-3.0.0.tgz",
|
||||||
"integrity": "sha512-gB9q+NOn3D62x8Akn7nykh2H0ArNehYflZ3sgGZNc8eB6Yf0HnK30vwpe0xXTLYMIe15XeRNiiZd8qwTFnGYSw==",
|
"integrity": "sha512-8DUdLnTiX0aBlKqEMPSKiwesx+nHjdH7Bi8+8Hlq0dKXurW9zJvhVfEDU9Pv/LOskNWGo/PuSzLnAcmFqKCvpg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-runtime": "^6.22.0"
|
"babel-runtime": "^6.22.0"
|
||||||
}
|
}
|
||||||
@ -118,14 +698,24 @@
|
|||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
"mergebounce": {
|
"mergebounce": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/mergebounce/-/mergebounce-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mergebounce/-/mergebounce-0.1.1.tgz",
|
||||||
"integrity": "sha512-M78tj1Ca6Q1QNmVexX+ExuX2Ta8OsNFT3h2RmPpYSUeXF+7/NyImQ/03NxpNzycvjWMNzSOX5uq7fzuj4Rgqrw==",
|
"integrity": "sha512-UvBRi+87aFdjLXAQhX3aTja4l5Uc4ZTrSL3V939lR7n7PWrBN8CiQLTprmtVca65Vwu2JuXPbMIvNM4JElMxdQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@converse/openpromise": "0.0.1",
|
"@converse/openpromise": "0.0.1",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||||
|
},
|
||||||
|
"picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||||
|
},
|
||||||
"pluggable.js": {
|
"pluggable.js": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-3.0.1.tgz",
|
||||||
@ -134,15 +724,32 @@
|
|||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"readdirp": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
|
"requires": {
|
||||||
|
"picomatch": "^2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||||
},
|
},
|
||||||
|
"rollup": {
|
||||||
|
"version": "3.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
||||||
|
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sizzle": {
|
"sizzle": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.10",
|
||||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
||||||
"integrity": "sha512-abtd95IkbcMAaYk1Lux4k9Xz6wnQqyLy2aco9HGJ8jVaCDEcc+ug0hW8RdV6aIre3ycWXxPdcX0u7QL/1UaSoA=="
|
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
||||||
},
|
},
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -150,13 +757,22 @@
|
|||||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||||
},
|
},
|
||||||
"strophe.js": {
|
"strophe.js": {
|
||||||
"version": "1.4.2",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||||
"integrity": "sha512-jkyZQCZLm7Zgmra0zJKxpHPNIUncYj/e/eYfgxFoc5gwrWeHWigNBs0q7wtqhCiqG6Qxcf22PUpcyBq8cK+9ew==",
|
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@xmldom/xmldom": "0.8.3",
|
||||||
"abab": "^2.0.3",
|
"abab": "^2.0.3",
|
||||||
"ws": "^7.0.0",
|
"karma-rollup-preprocessor": "^7.0.8",
|
||||||
"xmldom": "0.5.0"
|
"ws": "^8.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"requires": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
@ -165,21 +781,16 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"urijs": {
|
"urijs": {
|
||||||
"version": "1.19.7",
|
"version": "1.19.11",
|
||||||
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
|
||||||
"integrity": "sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA=="
|
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.3.1",
|
"version": "8.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
|
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||||
"optional": true
|
"optional": true,
|
||||||
},
|
"requires": {}
|
||||||
"xmldom": {
|
|
||||||
"version": "0.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz",
|
|
||||||
"integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==",
|
|
||||||
"optional": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@converse/headless",
|
"name": "@converse/headless",
|
||||||
"version": "8.0.1",
|
"version": "10.1.6",
|
||||||
"description": "Converse.js Headless build",
|
"description": "Converse.js Headless build",
|
||||||
"author": "cmrd Senya <senya@riseup.net>",
|
"author": "JC Brand <jc@opkode.com>",
|
||||||
|
"contributors": [
|
||||||
|
"cmrd Senya <senya@riseup.net>"
|
||||||
|
],
|
||||||
"homepage": "https://conversejs.org",
|
"homepage": "https://conversejs.org",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/converse-headless.min.js",
|
"main": "dist/converse-headless.js",
|
||||||
"files": [
|
"module": "index.js",
|
||||||
"*.js",
|
|
||||||
"*.json",
|
|
||||||
"dist/",
|
|
||||||
"README.md",
|
|
||||||
"utils/*.js",
|
|
||||||
"templates/*.html",
|
|
||||||
"templates/*.svg"
|
|
||||||
],
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"converse.js",
|
"converse.js",
|
||||||
"XMPP",
|
"XMPP",
|
||||||
@ -26,7 +21,8 @@
|
|||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/conversejs/converse.js.git"
|
"url": "git+https://github.com/conversejs/converse.js.git",
|
||||||
|
"directory": "src/headless"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
@ -34,21 +30,19 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/conversejs/converse.js/issues"
|
"url": "https://github.com/conversejs/converse.js/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
|
||||||
"devDependencies": {},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@converse/skeletor": "0.0.5",
|
"@converse/openpromise": "^0.0.1",
|
||||||
"dayjs": "1.10.6",
|
"@converse/skeletor": "^0.0.8",
|
||||||
"filesize": "^7.0.0",
|
"dayjs": "^1.11.8",
|
||||||
"localforage": "^1.10.0",
|
"dompurify": "^2.3.1",
|
||||||
"localforage-driver-memory": "^1.0.5",
|
"filesize": "^10.0.7",
|
||||||
"localforage-setitems": "^1.4.0",
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
"localforage-webextensionstorage-driver": "^2.0.0",
|
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"pluggable.js": "3.0.1",
|
"pluggable.js": "3.0.1",
|
||||||
"sizzle": "^2.3.5",
|
"sizzle": "^2.3.5",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"strophe.js": "1.4.2",
|
"strophe.js": "^1.6.2",
|
||||||
"urijs": "^1.19.7"
|
"urijs": "^1.19.10"
|
||||||
}
|
},
|
||||||
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
import { converse } from "../core.js";
|
|
||||||
import log from "@converse/headless/log";
|
|
||||||
import sizzle from 'sizzle';
|
|
||||||
import { getAttributes } from '@converse/headless/shared/parsers';
|
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
|
||||||
let _converse, api;
|
|
||||||
|
|
||||||
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
|
|
||||||
|
|
||||||
|
|
||||||
function parseForCommands (stanza) {
|
|
||||||
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
|
|
||||||
return items.map(getAttributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const adhoc_api = {
|
|
||||||
/**
|
|
||||||
* The XEP-0050 Ad-Hoc Commands API
|
|
||||||
*
|
|
||||||
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
|
|
||||||
*
|
|
||||||
* @namespace api.adhoc
|
|
||||||
* @memberOf api
|
|
||||||
*/
|
|
||||||
adhoc: {
|
|
||||||
/**
|
|
||||||
* @method api.adhoc.getCommands
|
|
||||||
* @param { String } to_jid
|
|
||||||
*/
|
|
||||||
async getCommands (to_jid) {
|
|
||||||
let commands = [];
|
|
||||||
try {
|
|
||||||
commands = parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
|
|
||||||
} catch (e) {
|
|
||||||
if (e === null) {
|
|
||||||
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
|
|
||||||
} else {
|
|
||||||
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
|
|
||||||
log.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-adhoc', {
|
|
||||||
|
|
||||||
dependencies: ["converse-disco"],
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
_converse = this._converse;
|
|
||||||
api = _converse.api;
|
|
||||||
Object.assign(api, adhoc_api);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default adhoc_api;
|
|
109
src/headless/plugins/adhoc/api.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import log from '@converse/headless/log';
|
||||||
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
import { getCommandFields, parseForCommands } from './utils.js';
|
||||||
|
|
||||||
|
const { Strophe, $iq, u, stx } = converse.env;
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* The XEP-0050 Ad-Hoc Commands API
|
||||||
|
*
|
||||||
|
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
|
||||||
|
*
|
||||||
|
* @namespace api.adhoc
|
||||||
|
* @memberOf api
|
||||||
|
*/
|
||||||
|
adhoc: {
|
||||||
|
/**
|
||||||
|
* @method api.adhoc.getCommands
|
||||||
|
* @param { String } to_jid
|
||||||
|
*/
|
||||||
|
async getCommands (to_jid) {
|
||||||
|
try {
|
||||||
|
return parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
|
||||||
|
} catch (e) {
|
||||||
|
if (e === null) {
|
||||||
|
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
|
||||||
|
} else {
|
||||||
|
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method api.adhoc.fetchCommandForm
|
||||||
|
*/
|
||||||
|
async fetchCommandForm (command) {
|
||||||
|
const node = command.node;
|
||||||
|
const jid = command.jid;
|
||||||
|
const stanza = $iq({
|
||||||
|
'type': 'set',
|
||||||
|
'to': jid
|
||||||
|
}).c('command', {
|
||||||
|
'xmlns': Strophe.NS.ADHOC,
|
||||||
|
'node': node,
|
||||||
|
'action': 'execute'
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
return getCommandFields(await api.sendIQ(stanza), jid);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (e === null) {
|
||||||
|
log.error(`Error: timeout while trying to execute command for ${jid}`);
|
||||||
|
} else {
|
||||||
|
log.error(`Error while trying to execute command for ${jid}`);
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
const { __ } = _converse;
|
||||||
|
return {
|
||||||
|
instructions: __('An error occurred while trying to fetch the command form'),
|
||||||
|
fields: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method api.adhoc.runCommand
|
||||||
|
* @param { String } jid
|
||||||
|
* @param { String } sessionid
|
||||||
|
* @param { 'execute' | 'cancel' | 'prev' | 'next' | 'complete' } action
|
||||||
|
* @param { String } node
|
||||||
|
* @param { Array<{ string: string }> } inputs
|
||||||
|
*/
|
||||||
|
async runCommand (jid, sessionid, node, action, inputs) {
|
||||||
|
const iq =
|
||||||
|
stx`<iq type="set" to="${jid}" xmlns="jabber:client">
|
||||||
|
<command sessionid="${sessionid}" node="${node}" action="${action}" xmlns="${Strophe.NS.ADHOC}">
|
||||||
|
${ !['cancel', 'prev'].includes(action) ? stx`
|
||||||
|
<x xmlns="${Strophe.NS.XFORM}" type="submit">
|
||||||
|
${ inputs.reduce((out, { name, value }) => out + `<field var="${name}"><value>${value}</value></field>`, '') }
|
||||||
|
</x>` : '' }
|
||||||
|
</command>
|
||||||
|
</iq>`;
|
||||||
|
|
||||||
|
const result = await api.sendIQ(iq, null, false);
|
||||||
|
if (result === null) {
|
||||||
|
log.warn(`A timeout occurred while trying to run an ad-hoc command`);
|
||||||
|
const { __ } = _converse;
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
note: __('A timeout occurred'),
|
||||||
|
}
|
||||||
|
} else if (u.isErrorStanza(result)) {
|
||||||
|
log.error('Error while trying to execute an ad-hoc command');
|
||||||
|
log.error(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = result.querySelector('command');
|
||||||
|
const status = command?.getAttribute('status');
|
||||||
|
return {
|
||||||
|
status,
|
||||||
|
...(status === 'executing' ? getCommandFields(result) : {}),
|
||||||
|
note: result.querySelector('note')?.textContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/headless/plugins/adhoc/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import adhoc_api from './api.js';
|
||||||
|
import { converse } from "@converse/headless/core";
|
||||||
|
|
||||||
|
const { Strophe } = converse.env;
|
||||||
|
|
||||||
|
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
|
||||||
|
|
||||||
|
|
||||||
|
converse.plugins.add('converse-adhoc', {
|
||||||
|
|
||||||
|
dependencies: ["converse-disco"],
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
Object.assign(this._converse.api, adhoc_api);
|
||||||
|
}
|
||||||
|
});
|
22
src/headless/plugins/adhoc/utils.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import sizzle from 'sizzle';
|
||||||
|
import { converse } from "@converse/headless/core";
|
||||||
|
import { getAttributes } from '@converse/headless/shared/parsers';
|
||||||
|
|
||||||
|
const { Strophe, u } = converse.env;
|
||||||
|
|
||||||
|
export function parseForCommands (stanza) {
|
||||||
|
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
|
||||||
|
return items.map(getAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommandFields (iq, jid) {
|
||||||
|
const cmd_el = sizzle(`command[xmlns="${Strophe.NS.ADHOC}"]`, iq).pop();
|
||||||
|
const data = {
|
||||||
|
sessionid: cmd_el.getAttribute('sessionid'),
|
||||||
|
instructions: sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()?.textContent,
|
||||||
|
fields: sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el)
|
||||||
|
.map(f => u.xForm2TemplateResult(f, cmd_el, { domain: jid })),
|
||||||
|
actions: Array.from(cmd_el.querySelector('actions')?.children).map((a) => a.nodeName.toLowerCase()) ?? []
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import "@converse/headless/plugins/muc/index.js";
|
import "@converse/headless/plugins/muc/index.js";
|
||||||
import Bookmark from './model.js';
|
import Bookmark from './model.js';
|
||||||
import log from "@converse/headless/log.js";
|
import log from "@converse/headless/log.js";
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
@ -14,7 +13,7 @@ const Bookmarks = {
|
|||||||
model: Bookmark,
|
model: Bookmark,
|
||||||
comparator: (item) => item.get('name').toLowerCase(),
|
comparator: (item) => item.get('name').toLowerCase(),
|
||||||
|
|
||||||
initialize () {
|
async initialize () {
|
||||||
this.on('add', bm => this.openBookmarkedRoom(bm)
|
this.on('add', bm => this.openBookmarkedRoom(bm)
|
||||||
.then(bm => this.markRoomAsBookmarked(bm))
|
.then(bm => this.markRoomAsBookmarked(bm))
|
||||||
.catch(e => log.fatal(e))
|
.catch(e => log.fatal(e))
|
||||||
@ -26,6 +25,17 @@ const Bookmarks = {
|
|||||||
const cache_key = `converse.room-bookmarks${_converse.bare_jid}`;
|
const cache_key = `converse.room-bookmarks${_converse.bare_jid}`;
|
||||||
this.fetched_flag = cache_key+'fetched';
|
this.fetched_flag = cache_key+'fetched';
|
||||||
initStorage(this, cache_key);
|
initStorage(this, cache_key);
|
||||||
|
|
||||||
|
await this.fetchBookmarks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered once the _converse.Bookmarks collection
|
||||||
|
* has been created and cached bookmarks have been fetched.
|
||||||
|
* @event _converse#bookmarksInitialized
|
||||||
|
* @type { _converse.Bookmarks }
|
||||||
|
* @example _converse.api.listen.on('bookmarksInitialized', (bookmarks) => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('bookmarksInitialized', this);
|
||||||
},
|
},
|
||||||
|
|
||||||
async openBookmarkedRoom (bookmark) {
|
async openBookmarkedRoom (bookmark) {
|
||||||
@ -86,12 +96,13 @@ const Bookmarks = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onBookmarkError (iq, options) {
|
onBookmarkError (iq, options) {
|
||||||
|
const { __ } = _converse;
|
||||||
log.error("Error while trying to add bookmark");
|
log.error("Error while trying to add bookmark");
|
||||||
log.error(iq);
|
log.error(iq);
|
||||||
api.alert(
|
api.alert(
|
||||||
'error', __('Error'), [__("Sorry, something went wrong while trying to save your bookmark.")]
|
'error', __('Error'), [__("Sorry, something went wrong while trying to save your bookmark.")]
|
||||||
);
|
);
|
||||||
this.findWhere({'jid': options.jid}).destroy();
|
this.get(options.jid)?.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchBookmarksFromServer (deferred) {
|
fetchBookmarksFromServer (deferred) {
|
||||||
@ -108,16 +119,12 @@ const Bookmarks = {
|
|||||||
|
|
||||||
markRoomAsBookmarked (bookmark) {
|
markRoomAsBookmarked (bookmark) {
|
||||||
const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
|
const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
|
||||||
if (groupchat !== undefined) {
|
groupchat?.save('bookmarked', true);
|
||||||
groupchat.save('bookmarked', true);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
markRoomAsUnbookmarked (bookmark) {
|
markRoomAsUnbookmarked (bookmark) {
|
||||||
const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
|
const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
|
||||||
if (groupchat !== undefined) {
|
groupchat?.save('bookmarked', false);
|
||||||
groupchat.save('bookmarked', false);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createBookmarksFromStanza (stanza) {
|
createBookmarksFromStanza (stanza) {
|
||||||
@ -145,6 +152,7 @@ const Bookmarks = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onBookmarksReceivedError (deferred, iq) {
|
onBookmarksReceivedError (deferred, iq) {
|
||||||
|
const { __ } = _converse;
|
||||||
if (iq === null) {
|
if (iq === null) {
|
||||||
log.error('Error: timeout while fetching bookmarks');
|
log.error('Error: timeout while fetching bookmarks');
|
||||||
api.alert('error', __('Timeout Error'),
|
api.alert('error', __('Timeout Error'),
|
||||||
@ -167,7 +175,9 @@ const Bookmarks = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getUnopenedBookmarks () {
|
async getUnopenedBookmarks () {
|
||||||
|
await api.waitUntil('bookmarksInitialized')
|
||||||
|
await api.waitUntil('chatBoxesFetched')
|
||||||
return this.filter(b => !_converse.chatboxes.get(b.get('jid')));
|
return this.filter(b => !_converse.chatboxes.get(b.get('jid')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,64 +1,36 @@
|
|||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* Converse.js plugin which adds views for bookmarks specified in XEP-0048.
|
* Converse.js plugin which adds views for bookmarks specified in XEP-0048.
|
||||||
* @copyright 2020, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import "@converse/headless/plugins/muc/index.js";
|
import "@converse/headless/plugins/muc/index.js";
|
||||||
import Bookmark from './model.js';
|
import Bookmark from './model.js';
|
||||||
import Bookmarks from './collection.js';
|
import Bookmarks from './collection.js';
|
||||||
import log from "@converse/headless/log.js";
|
import { Collection } from "@converse/skeletor/src/collection.js";
|
||||||
import { Collection } from "@converse/skeletor/src/collection";
|
import { _converse, api, converse } from "@converse/headless/core.js";
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { initBookmarks, getNicknameFromBookmark, handleBookmarksPush } from './utils.js';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
|
||||||
import { initBookmarks, getNicknameFromBookmark } from './utils.js';
|
|
||||||
|
|
||||||
const { Strophe, sizzle } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
|
|
||||||
Strophe.addNamespace('BOOKMARKS', 'storage:bookmarks');
|
Strophe.addNamespace('BOOKMARKS', 'storage:bookmarks');
|
||||||
|
|
||||||
|
|
||||||
function handleBookmarksPush (message) {
|
|
||||||
if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"] items[node="${Strophe.NS.BOOKMARKS}"]`, message).length) {
|
|
||||||
api.waitUntil('bookmarksInitialized')
|
|
||||||
.then(() => _converse.bookmarks.createBookmarksFromStanza(message))
|
|
||||||
.catch(e => log.fatal(e));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-bookmarks', {
|
converse.plugins.add('converse-bookmarks', {
|
||||||
|
|
||||||
/* Plugin dependencies are other plugins which might be
|
|
||||||
* overridden or relied upon, and therefore need to be loaded before
|
|
||||||
* this plugin.
|
|
||||||
*
|
|
||||||
* If the setting "strict_plugin_dependencies" is set to true,
|
|
||||||
* an error will be raised if the plugin is not found. By default it's
|
|
||||||
* false, which means these plugins are only loaded opportunistically.
|
|
||||||
*
|
|
||||||
* NB: These plugins need to have already been loaded via require.js.
|
|
||||||
*/
|
|
||||||
dependencies: ["converse-chatboxes", "converse-muc"],
|
dependencies: ["converse-chatboxes", "converse-muc"],
|
||||||
|
|
||||||
overrides: {
|
overrides: {
|
||||||
// Overrides mentioned here will be picked up by converse.js's
|
// Overrides mentioned here will be picked up by converse.js's
|
||||||
// plugin architecture they will replace existing methods on the
|
// plugin architecture they will replace existing methods on the
|
||||||
// relevant objects or classes.
|
// relevant objects or classes.
|
||||||
//
|
|
||||||
// New functions which don't exist yet can also be added.
|
// New functions which don't exist yet can also be added.
|
||||||
|
|
||||||
ChatRoom: {
|
ChatRoom: {
|
||||||
getDisplayName () {
|
getDisplayName () {
|
||||||
const { _converse } = this.__super__;
|
const { _converse, getDisplayName } = this.__super__;
|
||||||
if (this.get('bookmarked') && _converse.bookmarks) {
|
const bookmark = this.get('bookmarked') ? _converse.bookmarks?.get(this.get('jid')) : null;
|
||||||
const bookmark = _converse.bookmarks.findWhere({'jid': this.get('jid')});
|
return bookmark?.get('name') || getDisplayName.apply(this, arguments);
|
||||||
if (bookmark) {
|
|
||||||
return bookmark.get('name');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.__super__.getDisplayName.apply(this, arguments);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getAndPersistNickname (nick) {
|
getAndPersistNickname (nick) {
|
||||||
@ -69,10 +41,6 @@ converse.plugins.add('converse-bookmarks', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
/* The initialize function gets called as soon as the plugin is
|
|
||||||
* loaded by converse.js's plugin machinery.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Configuration values for this plugin
|
// Configuration values for this plugin
|
||||||
// ====================================
|
// ====================================
|
||||||
// Refer to docs/source/configuration.rst for explanations of these
|
// Refer to docs/source/configuration.rst for explanations of these
|
||||||
@ -88,12 +56,6 @@ converse.plugins.add('converse-bookmarks', {
|
|||||||
_converse.Bookmark = Bookmark;
|
_converse.Bookmark = Bookmark;
|
||||||
_converse.Bookmarks = Collection.extend(Bookmarks);
|
_converse.Bookmarks = Collection.extend(Bookmarks);
|
||||||
|
|
||||||
_converse.BookmarksList = Model.extend({
|
|
||||||
defaults: {
|
|
||||||
"toggle-state": _converse.OPENED
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
api.listen.on('addClientFeatures', () => {
|
api.listen.on('addClientFeatures', () => {
|
||||||
if (api.settings.get('allow_bookmarks')) {
|
if (api.settings.get('allow_bookmarks')) {
|
||||||
api.disco.own.features.add(Strophe.NS.BOOKMARKS + '+notify')
|
api.disco.own.features.add(Strophe.NS.BOOKMARKS + '+notify')
|
||||||
@ -101,7 +63,7 @@ converse.plugins.add('converse-bookmarks', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
api.listen.on('clearSession', () => {
|
api.listen.on('clearSession', () => {
|
||||||
if (_converse.bookmarks !== undefined) {
|
if (_converse.bookmarks) {
|
||||||
_converse.bookmarks.clearStore({'silent': true});
|
_converse.bookmarks.clearStore({'silent': true});
|
||||||
window.sessionStorage.removeItem(_converse.bookmarks.fetched_flag);
|
window.sessionStorage.removeItem(_converse.bookmarks.fetched_flag);
|
||||||
delete _converse.bookmarks;
|
delete _converse.bookmarks;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import log from "@converse/headless/log.js";
|
||||||
const { Strophe } = converse.env;
|
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||||
|
|
||||||
|
const { Strophe, sizzle } = converse.env;
|
||||||
|
|
||||||
export async function checkBookmarksSupport () {
|
export async function checkBookmarksSupport () {
|
||||||
const identity = await api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid);
|
const identity = await api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid);
|
||||||
if (_converse.allow_public_bookmarks) {
|
if (api.settings.get('allow_public_bookmarks')) {
|
||||||
return !!identity;
|
return !!identity;
|
||||||
} else {
|
} else {
|
||||||
return api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', _converse.bare_jid);
|
return api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', _converse.bare_jid);
|
||||||
@ -16,27 +18,21 @@ export async function initBookmarks () {
|
|||||||
}
|
}
|
||||||
if (await checkBookmarksSupport()) {
|
if (await checkBookmarksSupport()) {
|
||||||
_converse.bookmarks = new _converse.Bookmarks();
|
_converse.bookmarks = new _converse.Bookmarks();
|
||||||
await _converse.bookmarks.fetchBookmarks();
|
|
||||||
/**
|
|
||||||
* Triggered once the _converse.Bookmarks collection
|
|
||||||
* has been created and cached bookmarks have been fetched.
|
|
||||||
* @event _converse#bookmarksInitialized
|
|
||||||
* @example _converse.api.listen.on('bookmarksInitialized', () => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('bookmarksInitialized');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user has a bookmark with a saved nickanme
|
|
||||||
* for this groupchat and return it.
|
|
||||||
*/
|
|
||||||
export function getNicknameFromBookmark (jid) {
|
export function getNicknameFromBookmark (jid) {
|
||||||
if (!_converse.bookmarks || !api.settings.get('allow_bookmarks')) {
|
if (!api.settings.get('allow_bookmarks')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const bookmark = _converse.bookmarks.findWhere({'jid': jid});
|
return _converse.bookmarks?.get(jid)?.get('nick');
|
||||||
if (bookmark) {
|
}
|
||||||
return bookmark.get('nick');
|
|
||||||
}
|
export function handleBookmarksPush (message) {
|
||||||
|
if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"] items[node="${Strophe.NS.BOOKMARKS}"]`, message).length) {
|
||||||
|
api.waitUntil('bookmarksInitialized')
|
||||||
|
.then(() => _converse.bookmarks.createBookmarksFromStanza(message))
|
||||||
|
.catch(e => log.fatal(e));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* @module converse-bosh
|
|
||||||
* @copyright The Converse.js contributors
|
* @copyright The Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
* @description Converse.js plugin which add support for XEP-0206: XMPP Over BOSH
|
* @description Converse.js plugin which add support for XEP-0206: XMPP Over BOSH
|
||||||
*/
|
*/
|
||||||
import 'strophe.js/src/bosh';
|
import 'strophe.js/src/bosh';
|
||||||
|
import log from "../log.js";
|
||||||
|
import { BOSH_WAIT } from '@converse/headless/shared/constants.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api, converse } from "../core.js";
|
import { _converse, api, converse } from "../core.js";
|
||||||
import log from "../log.js";
|
import { setUserJID, } from '@converse/headless/utils/init.js';
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
|
|
||||||
@ -36,13 +37,13 @@ converse.plugins.add('converse-bosh', {
|
|||||||
}
|
}
|
||||||
if (_converse.jid) {
|
if (_converse.jid) {
|
||||||
if (_converse.bosh_session.get('jid') !== _converse.jid) {
|
if (_converse.bosh_session.get('jid') !== _converse.jid) {
|
||||||
const jid = await _converse.setUserJID(_converse.jid);
|
const jid = await setUserJID(_converse.jid);
|
||||||
_converse.bosh_session.clear({'silent': true });
|
_converse.bosh_session.clear({'silent': true });
|
||||||
_converse.bosh_session.save({jid});
|
_converse.bosh_session.save({jid});
|
||||||
}
|
}
|
||||||
} else { // Keepalive
|
} else { // Keepalive
|
||||||
const jid = _converse.bosh_session.get('jid');
|
const jid = _converse.bosh_session.get('jid');
|
||||||
jid && await _converse.setUserJID(jid);
|
jid && await setUserJID(jid);
|
||||||
}
|
}
|
||||||
return _converse.bosh_session;
|
return _converse.bosh_session;
|
||||||
}
|
}
|
||||||
@ -58,12 +59,13 @@ converse.plugins.add('converse-bosh', {
|
|||||||
xhr.onload = async function () {
|
xhr.onload = async function () {
|
||||||
if (xhr.status >= 200 && xhr.status < 400) {
|
if (xhr.status >= 200 && xhr.status < 400) {
|
||||||
const data = JSON.parse(xhr.responseText);
|
const data = JSON.parse(xhr.responseText);
|
||||||
const jid = await _converse.setUserJID(data.jid);
|
const jid = await setUserJID(data.jid);
|
||||||
_converse.connection.attach(
|
_converse.connection.attach(
|
||||||
jid,
|
jid,
|
||||||
data.sid,
|
data.sid,
|
||||||
data.rid,
|
data.rid,
|
||||||
_converse.connection.onConnectStatusChanged
|
_converse.connection.onConnectStatusChanged,
|
||||||
|
BOSH_WAIT
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
xhr.onerror();
|
xhr.onerror();
|
||||||
@ -134,7 +136,7 @@ converse.plugins.add('converse-bosh', {
|
|||||||
tokens: {
|
tokens: {
|
||||||
/**
|
/**
|
||||||
* @method api.tokens.get
|
* @method api.tokens.get
|
||||||
* @param {string} [id] The type of token to return ('rid' or 'sid').
|
* @param { string } [id] The type of token to return ('rid' or 'sid').
|
||||||
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
|
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
|
||||||
* @example _converse.api.tokens.get('rid');
|
* @example _converse.api.tokens.get('rid');
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2020, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import { api, converse } from '@converse/headless/core';
|
import { api, converse } from '@converse/headless/core';
|
||||||
import { createCapsNode } from './utils.js';
|
import { addCapsNode } from './utils.js';
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ converse.plugins.add('converse-caps', {
|
|||||||
dependencies: ['converse-status'],
|
dependencies: ['converse-status'],
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
api.listen.on('constructedPresence', (_, p) => (p.root().cnode(createCapsNode()).up() && p));
|
api.listen.on('constructedPresence', (_, p) => addCapsNode(p));
|
||||||
api.listen.on('constructedMUCPresence', (_, p) => (p.root().cnode(createCapsNode()).up() && p));
|
api.listen.on('constructedMUCPresence', (_, p) => addCapsNode(p));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import SHA1 from 'strophe.js/src/sha1';
|
import { _converse, converse } from '@converse/headless/core.js';
|
||||||
import { _converse, converse } from '@converse/headless/core';
|
import { arrayBufferToBase64, stringToArrayBuffer } from '@converse/headless/utils/arraybuffer.js';
|
||||||
|
|
||||||
const { Strophe, $build } = converse.env;
|
const { Strophe, $build } = converse.env;
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ function propertySort (array, property) {
|
|||||||
return array.sort((a, b) => { return a[property] > b[property] ? -1 : 1 });
|
return array.sort((a, b) => { return a[property] > b[property] ? -1 : 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateVerificationString () {
|
async function generateVerificationString () {
|
||||||
const identities = _converse.api.disco.own.identities.get();
|
const identities = _converse.api.disco.own.identities.get();
|
||||||
const features = _converse.api.disco.own.features.get();
|
const features = _converse.api.disco.own.features.get();
|
||||||
|
|
||||||
@ -20,14 +20,27 @@ function generateVerificationString () {
|
|||||||
let S = identities.reduce((result, id) => `${result}${id.category}/${id.type}/${id?.lang ?? ''}/${id.name}<`, "");
|
let S = identities.reduce((result, id) => `${result}${id.category}/${id.type}/${id?.lang ?? ''}/${id.name}<`, "");
|
||||||
features.sort();
|
features.sort();
|
||||||
S = features.reduce((result, feature) => `${result}${feature}<`, S);
|
S = features.reduce((result, feature) => `${result}${feature}<`, S);
|
||||||
return SHA1.b64_sha1(S);
|
|
||||||
|
const ab = await crypto.subtle.digest('SHA-1', stringToArrayBuffer(S));
|
||||||
|
return arrayBufferToBase64(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCapsNode () {
|
async function createCapsNode () {
|
||||||
return $build("c", {
|
return $build("c", {
|
||||||
'xmlns': Strophe.NS.CAPS,
|
'xmlns': Strophe.NS.CAPS,
|
||||||
'hash': "sha-1",
|
'hash': "sha-1",
|
||||||
'node': "https://conversejs.org",
|
'node': "https://conversejs.org",
|
||||||
'ver': generateVerificationString()
|
'ver': await generateVerificationString()
|
||||||
}).nodeTree;
|
}).tree();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a stanza, adds a XEP-0115 CAPS element
|
||||||
|
* @param { Element } stanza
|
||||||
|
*/
|
||||||
|
export async function addCapsNode (stanza) {
|
||||||
|
const caps_el = await createCapsNode();
|
||||||
|
stanza.root().cnode(caps_el).up();
|
||||||
|
return stanza;
|
||||||
}
|
}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* @module converse-carbons
|
|
||||||
* @copyright The Converse.js contributors
|
|
||||||
* @license Mozilla Public License (MPLv2)
|
|
||||||
* @description Implements support for XEP-0280 Message Carbons
|
|
||||||
*/
|
|
||||||
|
|
||||||
import log from '@converse/headless/log';
|
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
|
||||||
import { _converse, api, converse } from "../core.js";
|
|
||||||
|
|
||||||
|
|
||||||
/* Ask the XMPP server to enable Message Carbons
|
|
||||||
* See XEP-0280 https://xmpp.org/extensions/xep-0280.html#enabling
|
|
||||||
*/
|
|
||||||
function enableCarbons (reconnecting) {
|
|
||||||
if (reconnecting) {
|
|
||||||
_converse.session?.set({'carbons_enabled': false})
|
|
||||||
}
|
|
||||||
if (!api.settings.get("message_carbons") || _converse.session?.get('carbons_enabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const carbons_iq = new Strophe.Builder('iq', {
|
|
||||||
'from': _converse.connection.jid,
|
|
||||||
'id': 'enablecarbons',
|
|
||||||
'type': 'set'
|
|
||||||
})
|
|
||||||
.c('enable', {xmlns: Strophe.NS.CARBONS});
|
|
||||||
|
|
||||||
_converse.connection.addHandler((iq) => {
|
|
||||||
if (iq.querySelectorAll('error').length > 0) {
|
|
||||||
log.warn('An error occurred while trying to enable message carbons.');
|
|
||||||
} else {
|
|
||||||
_converse.session.set({'carbons_enabled': true});
|
|
||||||
log.debug('Message carbons have been enabled.');
|
|
||||||
}
|
|
||||||
_converse.session.save(); // Gather multiple sets into one save
|
|
||||||
}, null, "iq", null, "enablecarbons");
|
|
||||||
_converse.connection.send(carbons_iq);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-carbons', {
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
api.settings.extend({
|
|
||||||
message_carbons: true
|
|
||||||
});
|
|
||||||
|
|
||||||
api.listen.on('afterResourceBinding', enableCarbons);
|
|
||||||
}
|
|
||||||
});
|
|
@ -13,7 +13,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* @method api.chats.create
|
* @method api.chats.create
|
||||||
* @param {string|string[]} jid|jids An jid or array of jids
|
* @param {string|string[]} jid|jids An jid or array of jids
|
||||||
* @param {object} [attrs] An object containing configuration attributes.
|
* @param { object } [attrs] An object containing configuration attributes.
|
||||||
*/
|
*/
|
||||||
async create (jids, attrs) {
|
async create (jids, attrs) {
|
||||||
if (typeof jids === 'string') {
|
if (typeof jids === 'string') {
|
||||||
@ -44,9 +44,9 @@ export default {
|
|||||||
*
|
*
|
||||||
* @method api.chats.open
|
* @method api.chats.open
|
||||||
* @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
* @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||||
* @param {Boolean} [attrs.minimized] - Should the chat be created in minimized state.
|
* @param { Boolean } [attrs.minimized] - Should the chat be created in minimized state.
|
||||||
* @param {Boolean} [force=false] - By default, a minimized
|
* @param { Boolean } [force=false] - By default, a minimized
|
||||||
* chat won't be maximized (in `overlayed` view mode) and in
|
* chat won't be maximized (in `overlayed` view mode) and in
|
||||||
* `fullscreen` view mode a newly opened chat won't replace
|
* `fullscreen` view mode a newly opened chat won't replace
|
||||||
* another chat already in the foreground.
|
* another chat already in the foreground.
|
||||||
@ -102,8 +102,8 @@ export default {
|
|||||||
*
|
*
|
||||||
* @method api.chats.get
|
* @method api.chats.get
|
||||||
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||||
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
|
||||||
* @returns { Promise<_converse.ChatBox> }
|
* @returns { Promise<_converse.ChatBox> }
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
@ -1,29 +1,23 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2020, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import ChatBox from './model.js';
|
import ChatBox from './model.js';
|
||||||
import MessageMixin from './message.js';
|
import MessageMixin from './message.js';
|
||||||
import ModelWithContact from './model-with-contact.js';
|
import ModelWithContact from './model-with-contact.js';
|
||||||
import chat_api from './api.js';
|
import chat_api from './api.js';
|
||||||
import { Collection } from "@converse/skeletor/src/collection";
|
import { Collection } from '@converse/skeletor/src/collection';
|
||||||
import { _converse, api, converse } from '../../core.js';
|
import { _converse, api, converse } from '../../core.js';
|
||||||
import { autoJoinChats, handleMessageStanza, onClearSession, openChat, registerMessageHandlers } from './utils.js';
|
import {
|
||||||
|
autoJoinChats,
|
||||||
|
enableCarbons,
|
||||||
|
handleMessageStanza,
|
||||||
|
onClearSession,
|
||||||
|
openChat,
|
||||||
|
registerMessageHandlers,
|
||||||
|
} from './utils.js';
|
||||||
|
|
||||||
converse.plugins.add('converse-chat', {
|
converse.plugins.add('converse-chat', {
|
||||||
/* Optional dependencies are other plugins which might be
|
|
||||||
* overridden or relied upon, and therefore need to be loaded before
|
|
||||||
* this plugin. They are called "optional" because they might not be
|
|
||||||
* available, in which case any overrides applicable to them will be
|
|
||||||
* ignored.
|
|
||||||
*
|
|
||||||
* It's possible however to make optional dependencies non-optional.
|
|
||||||
* If the setting "strict_plugin_dependencies" is set to true,
|
|
||||||
* an error will be raised if the plugin is not found.
|
|
||||||
*
|
|
||||||
* NB: These plugins need to have already been loaded via require.js.
|
|
||||||
*/
|
|
||||||
dependencies: ['converse-chatboxes', 'converse-disco'],
|
dependencies: ['converse-chatboxes', 'converse-disco'],
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
@ -40,14 +34,14 @@ converse.plugins.add('converse-chat', {
|
|||||||
'filter_by_resource': false,
|
'filter_by_resource': false,
|
||||||
'prune_messages_above': undefined,
|
'prune_messages_above': undefined,
|
||||||
'pruning_behavior': 'unscrolled',
|
'pruning_behavior': 'unscrolled',
|
||||||
'send_chat_markers': ["received", "displayed", "acknowledged"],
|
'send_chat_markers': ['received', 'displayed', 'acknowledged'],
|
||||||
'send_chat_state_notifications': true,
|
'send_chat_state_notifications': true,
|
||||||
});
|
});
|
||||||
|
|
||||||
_converse.Message = ModelWithContact.extend(MessageMixin);
|
_converse.Message = ModelWithContact.extend(MessageMixin);
|
||||||
_converse.Messages = Collection.extend({
|
_converse.Messages = Collection.extend({
|
||||||
model: _converse.Message,
|
model: _converse.Message,
|
||||||
comparator: 'time'
|
comparator: 'time',
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(_converse, { ChatBox, handleMessageStanza });
|
Object.assign(_converse, { ChatBox, handleMessageStanza });
|
||||||
@ -58,5 +52,8 @@ converse.plugins.add('converse-chat', {
|
|||||||
api.listen.on('chatBoxesFetched', autoJoinChats);
|
api.listen.on('chatBoxesFetched', autoJoinChats);
|
||||||
api.listen.on('presencesInitialized', registerMessageHandlers);
|
api.listen.on('presencesInitialized', registerMessageHandlers);
|
||||||
api.listen.on('clearSession', onClearSession);
|
api.listen.on('clearSession', onClearSession);
|
||||||
}
|
|
||||||
|
api.listen.on('connected', () => enableCarbons());
|
||||||
|
api.listen.on('reconnected', () => enableCarbons());
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -4,11 +4,11 @@ import log from '../../log.js';
|
|||||||
import { _converse, api, converse } from '../../core.js';
|
import { _converse, api, converse } from '../../core.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const { Strophe, sizzle, u } = converse.env;
|
||||||
const { Strophe } = converse.env;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin which turns a `ModelWithContact` model into a non-MUC message. These can be either `chat` messages or `headline` messages.
|
* Mixin which turns a `ModelWithContact` model into a non-MUC message.
|
||||||
|
* These can be either `chat`, `normal` or `headline` messages.
|
||||||
* @mixin
|
* @mixin
|
||||||
* @namespace _converse.Message
|
* @namespace _converse.Message
|
||||||
* @memberOf _converse
|
* @memberOf _converse
|
||||||
@ -29,13 +29,14 @@ const MessageMixin = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.initialized = getOpenPromise();
|
this.initialized = getOpenPromise();
|
||||||
if (this.get('type') === 'chat') {
|
|
||||||
ModelWithContact.prototype.initialize.apply(this, arguments);
|
|
||||||
this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
|
|
||||||
}
|
|
||||||
if (this.get('file')) {
|
if (this.get('file')) {
|
||||||
this.on('change:put', this.uploadFile, this);
|
this.on('change:put', () => this.uploadFile());
|
||||||
}
|
}
|
||||||
|
// If `type` changes from `error` to `chat`, we want to set the contact. See #2733
|
||||||
|
this.on('change:type', () => this.setContact());
|
||||||
|
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
|
||||||
|
|
||||||
|
await this.setContact();
|
||||||
this.setTimerForEphemeralMessage();
|
this.setTimerForEphemeralMessage();
|
||||||
/**
|
/**
|
||||||
* Triggered once a {@link _converse.Message} has been created and initialized.
|
* Triggered once a {@link _converse.Message} has been created and initialized.
|
||||||
@ -47,25 +48,26 @@ const MessageMixin = {
|
|||||||
this.initialized.resolve();
|
this.initialized.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setContact () {
|
||||||
|
if (['chat', 'normal'].includes(this.get('type'))) {
|
||||||
|
ModelWithContact.prototype.initialize.apply(this, arguments);
|
||||||
|
this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an auto-destruct timer for this message, if it's is_ephemeral.
|
* Sets an auto-destruct timer for this message, if it's is_ephemeral.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.Message#setTimerForEphemeralMessage
|
* @method _converse.Message#setTimerForEphemeralMessage
|
||||||
* @returns { Boolean } - Indicates whether the message is
|
|
||||||
* ephemeral or not, and therefore whether the timer was set or not.
|
|
||||||
*/
|
*/
|
||||||
setTimerForEphemeralMessage () {
|
setTimerForEphemeralMessage () {
|
||||||
const setTimer = () => {
|
if (this.ephemeral_timer) {
|
||||||
this.ephemeral_timer = window.setTimeout(this.safeDestroy.bind(this), 10000);
|
clearTimeout(this.ephemeral_timer);
|
||||||
};
|
}
|
||||||
if (this.isEphemeral()) {
|
const is_ephemeral = this.isEphemeral();
|
||||||
setTimer();
|
if (is_ephemeral) {
|
||||||
return true;
|
const timeout = typeof is_ephemeral === "number" ? is_ephemeral : 10000;
|
||||||
} else {
|
this.ephemeral_timer = window.setTimeout(() => this.safeDestroy(), timeout);
|
||||||
this.on('change:is_ephemeral', () =>
|
|
||||||
this.isEphemeral() ? setTimer() : clearTimeout(this.ephemeral_timer)
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -99,7 +101,7 @@ const MessageMixin = {
|
|||||||
try {
|
try {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e);
|
log.warn(`safeDestroy: ${e}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -141,18 +143,15 @@ const MessageMixin = {
|
|||||||
}
|
}
|
||||||
const date = dayjs(this.get('time'));
|
const date = dayjs(this.get('time'));
|
||||||
return this.get('from') === prev_model.get('from') &&
|
return this.get('from') === prev_model.get('from') &&
|
||||||
!this.isMeCommand() &&
|
!this.isMeCommand() && !prev_model.isMeCommand() &&
|
||||||
!prev_model.isMeCommand() &&
|
!!this.get('is_encrypted') === !!prev_model.get('is_encrypted') &&
|
||||||
this.get('type') !== 'info' &&
|
this.get('type') === prev_model.get('type') && this.get('type') !== 'info' &&
|
||||||
prev_model.get('type') !== 'info' &&
|
|
||||||
date.isBefore(dayjs(prev_model.get('time')).add(10, 'minutes')) &&
|
date.isBefore(dayjs(prev_model.get('time')).add(10, 'minutes')) &&
|
||||||
!!this.get('is_encrypted') === !!prev_model.get('is_encrypted');
|
(this.get('type') === 'groupchat' ? this.get('occupant_id') === prev_model.get('occupant_id') : true);
|
||||||
},
|
},
|
||||||
|
|
||||||
getDisplayName () {
|
getDisplayName () {
|
||||||
if (this.get('type') === 'groupchat') {
|
if (this.contact) {
|
||||||
return this.get('nick');
|
|
||||||
} else if (this.contact) {
|
|
||||||
return this.contact.getDisplayName();
|
return this.contact.getDisplayName();
|
||||||
} else if (this.vcard) {
|
} else if (this.vcard) {
|
||||||
return this.vcard.getDisplayName();
|
return this.vcard.getDisplayName();
|
||||||
@ -162,11 +161,14 @@ const MessageMixin = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getMessageText () {
|
getMessageText () {
|
||||||
const { __ } = _converse;
|
|
||||||
if (this.get('is_encrypted')) {
|
if (this.get('is_encrypted')) {
|
||||||
|
const { __ } = _converse;
|
||||||
return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message');
|
return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message');
|
||||||
}
|
} else if (['groupchat', 'chat', 'normal'].includes(this.get('type'))) {
|
||||||
|
return this.get('body');
|
||||||
|
} else {
|
||||||
return this.get('message');
|
return this.get('message');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,6 +196,18 @@ const MessageMixin = {
|
|||||||
return api.sendIQ(iq);
|
return api.sendIQ(iq);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUploadRequestMetadata (stanza) {
|
||||||
|
const headers = sizzle(`slot[xmlns="${Strophe.NS.HTTPUPLOAD}"] put header`, stanza);
|
||||||
|
// https://xmpp.org/extensions/xep-0363.html#request
|
||||||
|
// TODO: Can't set the Cookie header in JavaScipt, instead cookies need
|
||||||
|
// to be manually set via document.cookie, so we're leaving it out here.
|
||||||
|
return {
|
||||||
|
'headers': headers
|
||||||
|
.map(h => ({ 'name': h.getAttribute('name'), 'value': h.textContent }))
|
||||||
|
.filter(h => ['Authorization', 'Expires'].includes(h.name))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async getRequestSlotURL () {
|
async getRequestSlotURL () {
|
||||||
const { __ } = _converse;
|
const { __ } = _converse;
|
||||||
let stanza;
|
let stanza;
|
||||||
@ -207,8 +221,9 @@ const MessageMixin = {
|
|||||||
'is_ephemeral': true
|
'is_ephemeral': true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const slot = stanza.querySelector('slot');
|
const slot = sizzle(`slot[xmlns="${Strophe.NS.HTTPUPLOAD}"]`, stanza).pop();
|
||||||
if (slot) {
|
if (slot) {
|
||||||
|
this.upload_metadata = this.getUploadRequestMetadata(stanza);
|
||||||
this.save({
|
this.save({
|
||||||
'get': slot.querySelector('get').getAttribute('url'),
|
'get': slot.querySelector('get').getAttribute('url'),
|
||||||
'put': slot.querySelector('put').getAttribute('url')
|
'put': slot.querySelector('put').getAttribute('url')
|
||||||
@ -224,6 +239,7 @@ const MessageMixin = {
|
|||||||
|
|
||||||
uploadFile () {
|
uploadFile () {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.onreadystatechange = async () => {
|
xhr.onreadystatechange = async () => {
|
||||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
log.info('Status: ' + xhr.status);
|
log.info('Status: ' + xhr.status);
|
||||||
@ -277,6 +293,7 @@ const MessageMixin = {
|
|||||||
};
|
};
|
||||||
xhr.open('PUT', this.get('put'), true);
|
xhr.open('PUT', this.get('put'), true);
|
||||||
xhr.setRequestHeader('Content-type', this.file.type);
|
xhr.setRequestHeader('Content-type', this.file.type);
|
||||||
|
this.upload_metadata.headers?.forEach(h => xhr.setRequestHeader(h.name, h.value));
|
||||||
xhr.send(this.file);
|
xhr.send(this.file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
import ModelWithContact from './model-with-contact.js';
|
import ModelWithContact from './model-with-contact.js';
|
||||||
import filesize from "filesize";
|
|
||||||
import isMatch from "lodash-es/isMatch";
|
import isMatch from "lodash-es/isMatch";
|
||||||
import isObject from "lodash-es/isObject";
|
import isObject from "lodash-es/isObject";
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import pick from "lodash-es/pick";
|
import pick from "lodash-es/pick";
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
|
import { TimeoutError } from '../../shared/errors.js';
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "../../core.js";
|
||||||
|
import { debouncedPruneHistory, handleCorrection } from '@converse/headless/shared/chat/utils.js';
|
||||||
|
import { filesize } from "filesize";
|
||||||
|
import { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
import { debouncedPruneHistory, pruneHistory } from '@converse/headless/shared/chat/utils.js';
|
import { isUniView, isEmptyMessage } from '../../utils/core.js';
|
||||||
import { getMediaURLs } from '@converse/headless/shared/parsers';
|
|
||||||
import { parseMessage } from './parsers.js';
|
import { parseMessage } from './parsers.js';
|
||||||
import { sendMarker } from '@converse/headless/shared/actions';
|
import { sendMarker } from '@converse/headless/shared/actions.js';
|
||||||
|
|
||||||
const { Strophe, $msg } = converse.env;
|
const { Strophe, $msg } = converse.env;
|
||||||
|
|
||||||
@ -30,7 +32,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
return {
|
return {
|
||||||
'bookmarked': false,
|
'bookmarked': false,
|
||||||
'chat_state': undefined,
|
'chat_state': undefined,
|
||||||
'hidden': _converse.isUniView() && !api.settings.get('singleton'),
|
'hidden': isUniView() && !api.settings.get('singleton'),
|
||||||
'message_type': 'chat',
|
'message_type': 'chat',
|
||||||
'nickname': undefined,
|
'nickname': undefined,
|
||||||
'num_unread': 0,
|
'num_unread': 0,
|
||||||
@ -61,7 +63,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
this.initMessages();
|
this.initMessages();
|
||||||
|
|
||||||
if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||||
this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid});
|
this.presence = _converse.presences.get(jid) || _converse.presences.create({ jid });
|
||||||
await this.setRosterContact(jid);
|
await this.setRosterContact(jid);
|
||||||
this.presence.on('change:show', item => this.onPresenceChanged(item));
|
this.presence.on('change:show', item => this.onPresenceChanged(item));
|
||||||
}
|
}
|
||||||
@ -90,17 +92,6 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
initMessages () {
|
initMessages () {
|
||||||
this.messages = this.getMessagesCollection();
|
this.messages = this.getMessagesCollection();
|
||||||
this.messages.fetched = getOpenPromise();
|
this.messages.fetched = getOpenPromise();
|
||||||
this.messages.fetched.then(() => {
|
|
||||||
this.pruneHistoryWhenScrolledDown();
|
|
||||||
/**
|
|
||||||
* Triggered whenever a `_converse.ChatBox` instance has fetched its messages from
|
|
||||||
* `sessionStorage` but **NOT** from the server.
|
|
||||||
* @event _converse#afterMessagesFetched
|
|
||||||
* @type {_converse.ChatBoxView | _converse.ChatRoomView}
|
|
||||||
* @example _converse.api.listen.on('afterMessagesFetched', view => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('afterMessagesFetched', this);
|
|
||||||
});
|
|
||||||
this.messages.chatbox = this;
|
this.messages.chatbox = this;
|
||||||
initStorage(this.messages, this.getMessagesCacheKey());
|
initStorage(this.messages, this.getMessagesCacheKey());
|
||||||
|
|
||||||
@ -129,14 +120,14 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
afterMessagesFetched (messages) {
|
afterMessagesFetched () {
|
||||||
this.most_recent_cached_message = messages ? this.getMostRecentMessage(messages) : null;
|
this.pruneHistoryWhenScrolledDown();
|
||||||
/**
|
/**
|
||||||
* Triggered whenever a `_converse.ChatBox` instance has fetched its messages from
|
* Triggered whenever a { @link _converse.ChatBox } or ${ @link _converse.ChatRoom }
|
||||||
* `sessionStorage` but **NOT** from the server.
|
* has fetched its messages from the local cache.
|
||||||
* @event _converse#afterMessagesFetched
|
* @event _converse#afterMessagesFetched
|
||||||
* @type {_converse.ChatBox | _converse.ChatRoom}
|
* @type { _converse.ChatBox| _converse.ChatRoom }
|
||||||
* @example _converse.api.listen.on('afterMessagesFetched', view => { ... });
|
* @example _converse.api.listen.on('afterMessagesFetched', (chat) => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('afterMessagesFetched', this);
|
api.trigger('afterMessagesFetched', this);
|
||||||
},
|
},
|
||||||
@ -146,7 +137,6 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
log.info(`Not re-fetching messages for ${this.get('jid')}`);
|
log.info(`Not re-fetching messages for ${this.get('jid')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.most_recent_cached_message = null;
|
|
||||||
this.messages.fetched_flag = true;
|
this.messages.fetched_flag = true;
|
||||||
const resolve = this.messages.fetched.resolve;
|
const resolve = this.messages.fetched.resolve;
|
||||||
this.messages.fetch({
|
this.messages.fetch({
|
||||||
@ -235,7 +225,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
this.notifications.set('chat_state', attrs.chat_state);
|
this.notifications.set('chat_state', attrs.chat_state);
|
||||||
}
|
}
|
||||||
if (u.shouldCreateMessage(attrs)) {
|
if (u.shouldCreateMessage(attrs)) {
|
||||||
const msg = this.handleCorrection(attrs) || await this.createMessage(attrs);
|
const msg = await handleCorrection(this, attrs) || await this.createMessage(attrs);
|
||||||
this.notifications.set({'chat_state': null});
|
this.notifications.set({'chat_state': null});
|
||||||
this.handleUnreadMessage(msg);
|
this.handleUnreadMessage(msg);
|
||||||
}
|
}
|
||||||
@ -245,7 +235,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
async onMessageUploadChanged (message) {
|
async onMessageUploadChanged (message) {
|
||||||
if (message.get('upload') === _converse.SUCCESS) {
|
if (message.get('upload') === _converse.SUCCESS) {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
'body': message.get('message'),
|
'body': message.get('body'),
|
||||||
'spoiler_hint': message.get('spoiler_hint'),
|
'spoiler_hint': message.get('spoiler_hint'),
|
||||||
'oob_url': message.get('oob_url')
|
'oob_url': message.get('oob_url')
|
||||||
|
|
||||||
@ -258,7 +248,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
onMessageAdded (message) {
|
onMessageAdded (message) {
|
||||||
if (api.settings.get('prune_messages_above') &&
|
if (api.settings.get('prune_messages_above') &&
|
||||||
(api.settings.get('pruning_behavior') === 'scrolled' || !this.ui.get('scrolled')) &&
|
(api.settings.get('pruning_behavior') === 'scrolled' || !this.ui.get('scrolled')) &&
|
||||||
!u.isEmptyMessage(message)
|
!isEmptyMessage(message)
|
||||||
) {
|
) {
|
||||||
debouncedPruneHistory(this);
|
debouncedPruneHistory(this);
|
||||||
}
|
}
|
||||||
@ -271,9 +261,9 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
this.messages.trigger('reset');
|
this.messages.trigger('reset');
|
||||||
log.error(e);
|
log.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
delete this.msg_chain;
|
// No point in fetching messages from the cache if it's been cleared.
|
||||||
delete this.messages.fetched_flag;
|
// Make sure to resolve the fetched promise to avoid freezes.
|
||||||
this.messages.fetched = getOpenPromise();
|
this.messages.fetched.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -351,7 +341,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
api.settings.get('pruning_behavior') === 'unscrolled' &&
|
api.settings.get('pruning_behavior') === 'unscrolled' &&
|
||||||
!this.ui.get('scrolled')
|
!this.ui.get('scrolled')
|
||||||
) {
|
) {
|
||||||
pruneHistory(this);
|
debouncedPruneHistory(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -379,11 +369,12 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async createMessageFromError (error) {
|
async createMessageFromError (error) {
|
||||||
if (error instanceof _converse.TimeoutError) {
|
if (error instanceof TimeoutError) {
|
||||||
const msg = await this.createMessage({
|
const msg = await this.createMessage({
|
||||||
'type': 'error',
|
'type': 'error',
|
||||||
'message': error.message,
|
'message': error.message,
|
||||||
'retry_event_id': error.retry_event_id
|
'retry_event_id': error.retry_event_id,
|
||||||
|
'is_ephemeral': 30000,
|
||||||
});
|
});
|
||||||
msg.error = error;
|
msg.error = error;
|
||||||
}
|
}
|
||||||
@ -440,10 +431,9 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getMostRecentMessage (messages) {
|
getMostRecentMessage () {
|
||||||
messages = messages || this.messages;
|
for (let i=this.messages.length-1; i>=0; i--) {
|
||||||
for (let i=messages.length-1; i>=0; i--) {
|
const message = this.messages.at(i);
|
||||||
const message = messages.at(i);
|
|
||||||
if (message.get('type') === this.get('message_type')) {
|
if (message.get('type') === this.get('message_type')) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -451,8 +441,22 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getUpdatedMessageAttributes (message, attrs) {
|
getUpdatedMessageAttributes (message, attrs) {
|
||||||
// Filter the attrs object, restricting it to only the `is_archived` key.
|
if (!attrs.error_type && message.get('error_type') === 'Decryption') {
|
||||||
return (({ is_archived }) => ({ is_archived }))(attrs)
|
// Looks like we have a failed decrypted message stored, and now
|
||||||
|
// we have a properly decrypted version of the same message.
|
||||||
|
// See issue: https://github.com/conversejs/converse.js/issues/2733#issuecomment-1035493594
|
||||||
|
return Object.assign({}, attrs, {
|
||||||
|
error_condition: undefined,
|
||||||
|
error_message: undefined,
|
||||||
|
error_text: undefined,
|
||||||
|
error_type: undefined,
|
||||||
|
is_archived: attrs.is_archived,
|
||||||
|
is_ephemeral: false,
|
||||||
|
is_error: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return { is_archived: attrs.is_archived };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateMessage (message, attrs) {
|
updateMessage (message, attrs) {
|
||||||
@ -510,9 +514,9 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
*/
|
*/
|
||||||
shouldShowErrorMessage (attrs) {
|
shouldShowErrorMessage (attrs) {
|
||||||
const msg = this.getMessageReferencedByError(attrs);
|
const msg = this.getMessageReferencedByError(attrs);
|
||||||
if (!msg && !attrs.body) {
|
if (!msg && attrs.chat_state) {
|
||||||
// If the error refers to a message not included in our store,
|
// If the error refers to a message not included in our store,
|
||||||
// and it doesn't have a <body> tag, we assume that this was a
|
// and it has a chat state tag, we assume that this was a
|
||||||
// CSI message (which we don't store).
|
// CSI message (which we don't store).
|
||||||
// See https://github.com/conversejs/converse.js/issues/1317
|
// See https://github.com/conversejs/converse.js/issues/1317
|
||||||
return;
|
return;
|
||||||
@ -592,46 +596,6 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the passed in message attributes represent a
|
|
||||||
* message which corrects a previously received message, or an
|
|
||||||
* older message which has already been corrected.
|
|
||||||
* In both cases, update the corrected message accordingly.
|
|
||||||
* @private
|
|
||||||
* @method _converse.ChatBox#handleCorrection
|
|
||||||
* @param { object } attrs - Attributes representing a received
|
|
||||||
* message, as returned by {@link parseMessage}
|
|
||||||
* @returns { _converse.Message|undefined } Returns the corrected
|
|
||||||
* message or `undefined` if not applicable.
|
|
||||||
*/
|
|
||||||
handleCorrection (attrs) {
|
|
||||||
if (!attrs.replace_id || !attrs.from) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message = this.messages.findWhere({'msgid': attrs.replace_id, 'from': attrs.from});
|
|
||||||
if (!message) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const older_versions = message.get('older_versions') || {};
|
|
||||||
if ((attrs.time < message.get('time')) && message.get('edited')) {
|
|
||||||
// This is an older message which has been corrected afterwards
|
|
||||||
older_versions[attrs.time] = attrs['message'];
|
|
||||||
message.save({'older_versions': older_versions});
|
|
||||||
} else {
|
|
||||||
// This is a correction of an earlier message we already received
|
|
||||||
if(Object.keys(older_versions).length) {
|
|
||||||
older_versions[message.get('edited')] = message.get('message');
|
|
||||||
}else {
|
|
||||||
older_versions[message.get('time')] = message.get('message');
|
|
||||||
}
|
|
||||||
attrs = Object.assign(attrs, {'older_versions': older_versions});
|
|
||||||
delete attrs['id']; // Delete id, otherwise a new cache entry gets created
|
|
||||||
attrs['time'] = message.get('time');
|
|
||||||
message.save(attrs);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an already cached message (if it exists) based on the
|
* Returns an already cached message (if it exists) based on the
|
||||||
* passed in attributes map.
|
* passed in attributes map.
|
||||||
@ -666,15 +630,16 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getMessageBodyQueryAttrs (attrs) {
|
getMessageBodyQueryAttrs (attrs) {
|
||||||
if (attrs.message && attrs.msgid) {
|
if (attrs.msgid) {
|
||||||
const query = {
|
const query = {
|
||||||
'from': attrs.from,
|
'from': attrs.from,
|
||||||
'msgid': attrs.msgid
|
'msgid': attrs.msgid
|
||||||
}
|
}
|
||||||
if (!attrs.is_encrypted) {
|
// XXX: Need to take XEP-428 <fallback> into consideration
|
||||||
|
if (!attrs.is_encrypted && attrs.body) {
|
||||||
// We can't match the message if it's a reflected
|
// We can't match the message if it's a reflected
|
||||||
// encrypted message (e.g. via MAM or in a MUC)
|
// encrypted message (e.g. via MAM or in a MUC)
|
||||||
query['message'] = attrs.message;
|
query['body'] = attrs.body;
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
@ -803,18 +768,20 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* @method _converse.ChatBox#createMessageStanza
|
* @method _converse.ChatBox#createMessageStanza
|
||||||
* @param { _converse.Message } message - The message object
|
* @param { _converse.Message } message - The message object
|
||||||
*/
|
*/
|
||||||
createMessageStanza (message) {
|
async createMessageStanza (message) {
|
||||||
const stanza = $msg({
|
const stanza = $msg({
|
||||||
'from': _converse.connection.jid,
|
'from': _converse.connection.jid,
|
||||||
'to': this.get('jid'),
|
'to': this.get('jid'),
|
||||||
'type': this.get('message_type'),
|
'type': this.get('message_type'),
|
||||||
'id': message.get('edited') && u.getUniqueId() || message.get('msgid'),
|
'id': message.get('edited') && u.getUniqueId() || message.get('msgid'),
|
||||||
}).c('body').t(message.get('message')).up()
|
}).c('body').t(message.get('body')).up()
|
||||||
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).root();
|
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).root();
|
||||||
|
|
||||||
if (message.get('type') === 'chat') {
|
if (message.get('type') === 'chat') {
|
||||||
stanza.c('request', {'xmlns': Strophe.NS.RECEIPTS}).root();
|
stanza.c('request', {'xmlns': Strophe.NS.RECEIPTS}).root();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!message.get('is_encrypted')) {
|
||||||
if (message.get('is_spoiler')) {
|
if (message.get('is_spoiler')) {
|
||||||
if (message.get('spoiler_hint')) {
|
if (message.get('spoiler_hint')) {
|
||||||
stanza.c('spoiler', {'xmlns': Strophe.NS.SPOILER}, message.get('spoiler_hint')).root();
|
stanza.c('spoiler', {'xmlns': Strophe.NS.SPOILER}, message.get('spoiler_hint')).root();
|
||||||
@ -838,24 +805,43 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
if (message.get('oob_url')) {
|
if (message.get('oob_url')) {
|
||||||
stanza.c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('oob_url')).root();
|
stanza.c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('oob_url')).root();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message.get('edited')) {
|
if (message.get('edited')) {
|
||||||
stanza.c('replace', {
|
stanza.c('replace', {
|
||||||
'xmlns': Strophe.NS.MESSAGE_CORRECT,
|
'xmlns': Strophe.NS.MESSAGE_CORRECT,
|
||||||
'id': message.get('msgid')
|
'id': message.get('msgid')
|
||||||
}).root();
|
}).root();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.get('origin_id')) {
|
if (message.get('origin_id')) {
|
||||||
stanza.c('origin-id', {'xmlns': Strophe.NS.SID, 'id': message.get('origin_id')}).root();
|
stanza.c('origin-id', {'xmlns': Strophe.NS.SID, 'id': message.get('origin_id')}).root();
|
||||||
}
|
}
|
||||||
return stanza;
|
stanza.root();
|
||||||
|
/**
|
||||||
|
* *Hook* which allows plugins to update an outgoing message stanza
|
||||||
|
* @event _converse#createMessageStanza
|
||||||
|
* @param { _converse.ChatBox | _converse.ChatRoom } - The chat from
|
||||||
|
* which this message stanza is being sent.
|
||||||
|
* @param { Object } data - Message data
|
||||||
|
* @param { _converse.Message | _converse.ChatRoomMessage } data.message
|
||||||
|
* The message object from which the stanza is created and which gets persisted to storage.
|
||||||
|
* @param { Strophe.Builder } data.stanza
|
||||||
|
* The stanza that will be sent out, as a Strophe.Builder object.
|
||||||
|
* You can use the Strophe.Builder functions to extend the stanza.
|
||||||
|
* See http://strophe.im/strophejs/doc/1.4.3/files/strophe-umd-js.html#Strophe.Builder.Functions
|
||||||
|
*/
|
||||||
|
const data = await api.hook('createMessageStanza', this, { message, stanza });
|
||||||
|
return data.stanza;
|
||||||
},
|
},
|
||||||
|
|
||||||
getOutgoingMessageAttributes (attrs) {
|
async getOutgoingMessageAttributes (attrs) {
|
||||||
|
await api.emojis.initialize();
|
||||||
const is_spoiler = !!this.get('composing_spoiler');
|
const is_spoiler = !!this.get('composing_spoiler');
|
||||||
const origin_id = u.getUniqueId();
|
const origin_id = u.getUniqueId();
|
||||||
const text = attrs?.body;
|
const text = attrs?.body;
|
||||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
const body = text ? u.shortnamesToUnicode(text) : undefined;
|
||||||
return Object.assign({}, attrs, {
|
attrs = Object.assign({}, attrs, {
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'fullname': _converse.xmppstatus.get('fullname'),
|
'fullname': _converse.xmppstatus.get('fullname'),
|
||||||
'id': origin_id,
|
'id': origin_id,
|
||||||
@ -870,7 +856,20 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
body,
|
body,
|
||||||
is_spoiler,
|
is_spoiler,
|
||||||
origin_id
|
origin_id
|
||||||
}, getMediaURLs(text));
|
}, getMediaURLsMetadata(text));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *Hook* which allows plugins to update the attributes of an outgoing message.
|
||||||
|
* These attributes get set on the { @link _converse.Message } or
|
||||||
|
* { @link _converse.ChatRoomMessage } and persisted to storage.
|
||||||
|
* @event _converse#getOutgoingMessageAttributes
|
||||||
|
* @param { _converse.ChatBox | _converse.ChatRoom } chat
|
||||||
|
* The chat from which this message will be sent.
|
||||||
|
* @param { MessageAttributes } attrs
|
||||||
|
* The message attributes, from which the stanza will be created.
|
||||||
|
*/
|
||||||
|
attrs = await api.hook('getOutgoingMessageAttributes', this, attrs);
|
||||||
|
return attrs;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -884,17 +883,14 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* @param { String } send_time - time when the message was sent
|
* @param { String } send_time - time when the message was sent
|
||||||
*/
|
*/
|
||||||
setEditable (attrs, send_time) {
|
setEditable (attrs, send_time) {
|
||||||
if (attrs.is_headline || u.isEmptyMessage(attrs) || attrs.sender !== 'me') {
|
if (attrs.is_headline || isEmptyMessage(attrs) || attrs.sender !== 'me') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (api.settings.get('allow_message_corrections') === 'all') {
|
if (api.settings.get('allow_message_corrections') === 'all') {
|
||||||
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
||||||
} else if ((api.settings.get('allow_message_corrections') === 'last') && (send_time > this.get('time_sent'))) {
|
} else if ((api.settings.get('allow_message_corrections') === 'last') && (send_time > this.get('time_sent'))) {
|
||||||
this.set({'time_sent': send_time});
|
this.set({'time_sent': send_time});
|
||||||
const msg = this.messages.findWhere({'editable': true});
|
this.messages.findWhere({'editable': true})?.save({'editable': false});
|
||||||
if (msg) {
|
|
||||||
msg.save({'editable': false});
|
|
||||||
}
|
|
||||||
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -905,7 +901,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* before the collection has been fetched.
|
* before the collection has been fetched.
|
||||||
* @async
|
* @async
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatBox#queueMessageCreation
|
* @method _converse.ChatBox#createMessage
|
||||||
* @param { Object } attrs
|
* @param { Object } attrs
|
||||||
*/
|
*/
|
||||||
async createMessage (attrs, options) {
|
async createMessage (attrs, options) {
|
||||||
@ -926,26 +922,39 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* chat.sendMessage({'body': 'hello world'});
|
* chat.sendMessage({'body': 'hello world'});
|
||||||
*/
|
*/
|
||||||
async sendMessage (attrs) {
|
async sendMessage (attrs) {
|
||||||
attrs = this.getOutgoingMessageAttributes(attrs);
|
attrs = await this.getOutgoingMessageAttributes(attrs);
|
||||||
let message = this.messages.findWhere('correcting')
|
let message = this.messages.findWhere('correcting')
|
||||||
if (message) {
|
if (message) {
|
||||||
const older_versions = message.get('older_versions') || {};
|
const older_versions = message.get('older_versions') || {};
|
||||||
older_versions[message.get('time')] = message.get('message');
|
const edited_time = message.get('edited') || message.get('time');
|
||||||
|
older_versions[edited_time] = message.getMessageText();
|
||||||
|
|
||||||
message.save({
|
message.save({
|
||||||
|
...pick(attrs, ['body', 'is_only_emojis', 'media_urls', 'references', 'is_encrypted']),
|
||||||
|
...{
|
||||||
'correcting': false,
|
'correcting': false,
|
||||||
'edited': (new Date()).toISOString(),
|
'edited': (new Date()).toISOString(),
|
||||||
'message': attrs.message,
|
'message': attrs.body,
|
||||||
'older_versions': older_versions,
|
'ogp_metadata': [],
|
||||||
'references': attrs.references,
|
|
||||||
'is_only_emojis': attrs.is_only_emojis,
|
|
||||||
'origin_id': u.getUniqueId(),
|
'origin_id': u.getUniqueId(),
|
||||||
'received': undefined
|
'received': undefined,
|
||||||
|
older_versions,
|
||||||
|
plaintext: attrs.is_encrypted ? attrs.message : undefined,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setEditable(attrs, (new Date()).toISOString());
|
this.setEditable(attrs, (new Date()).toISOString());
|
||||||
message = await this.createMessage(attrs);
|
message = await this.createMessage(attrs);
|
||||||
}
|
}
|
||||||
api.send(this.createMessageStanza(message));
|
|
||||||
|
try {
|
||||||
|
const stanza = await this.createMessageStanza(message);
|
||||||
|
api.send(stanza);
|
||||||
|
} catch (e) {
|
||||||
|
message.destroy();
|
||||||
|
log.error(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when a message is being sent out
|
* Triggered when a message is being sent out
|
||||||
@ -1013,6 +1022,10 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* *Hook* which allows plugins to transform files before they'll be
|
* *Hook* which allows plugins to transform files before they'll be
|
||||||
* uploaded. The main use-case is to encrypt the files.
|
* uploaded. The main use-case is to encrypt the files.
|
||||||
* @event _converse#beforeFileUpload
|
* @event _converse#beforeFileUpload
|
||||||
|
* @param { _converse.ChatBox | _converse.ChatRoom } chat
|
||||||
|
* The chat from which this file will be uploaded.
|
||||||
|
* @param { File } file
|
||||||
|
* The file that will be uploaded
|
||||||
*/
|
*/
|
||||||
file = await api.hook('beforeFileUpload', this, file);
|
file = await api.hook('beforeFileUpload', this, file);
|
||||||
|
|
||||||
@ -1024,8 +1037,8 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
'is_ephemeral': true
|
'is_ephemeral': true
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const attrs = Object.assign(
|
const initial_attrs = await this.getOutgoingMessageAttributes();
|
||||||
this.getOutgoingMessageAttributes(), {
|
const attrs = Object.assign(initial_attrs, {
|
||||||
'file': true,
|
'file': true,
|
||||||
'progress': 0,
|
'progress': 0,
|
||||||
'slot_request_url': slot_request_url
|
'slot_request_url': slot_request_url
|
||||||
@ -1040,7 +1053,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
maybeShow (force) {
|
maybeShow (force) {
|
||||||
if (_converse.isUniView()) {
|
if (isUniView()) {
|
||||||
const filter = c => !c.get('hidden') &&
|
const filter = c => !c.get('hidden') &&
|
||||||
c.get('jid') !== this.get('jid') &&
|
c.get('jid') !== this.get('jid') &&
|
||||||
c.get('id') !== 'controlbox';
|
c.get('id') !== 'controlbox';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import u from '@converse/headless/utils/core';
|
import u from '@converse/headless/utils/core';
|
||||||
import { api, converse } from '@converse/headless/core';
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
import { rejectMessage } from '@converse/headless/shared/actions';
|
import { rejectMessage } from '@converse/headless/shared/actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
getCorrectionAttributes,
|
getCorrectionAttributes,
|
||||||
getEncryptionAttributes,
|
getEncryptionAttributes,
|
||||||
getErrorAttributes,
|
getErrorAttributes,
|
||||||
getMediaURLs,
|
getMediaURLsMetadata,
|
||||||
getOutOfBandAttributes,
|
getOutOfBandAttributes,
|
||||||
getReceiptId,
|
getReceiptId,
|
||||||
getReferences,
|
getReferences,
|
||||||
@ -32,11 +32,11 @@ const { Strophe, sizzle } = converse.env;
|
|||||||
/**
|
/**
|
||||||
* Parses a passed in message stanza and returns an object of attributes.
|
* Parses a passed in message stanza and returns an object of attributes.
|
||||||
* @method st#parseMessage
|
* @method st#parseMessage
|
||||||
* @param { XMLElement } stanza - The message stanza
|
* @param { Element } stanza - The message stanza
|
||||||
* @param { _converse } _converse
|
* @param { _converse } _converse
|
||||||
* @returns { (MessageAttributes|Error) }
|
* @returns { (MessageAttributes|Error) }
|
||||||
*/
|
*/
|
||||||
export async function parseMessage (stanza, _converse) {
|
export async function parseMessage (stanza) {
|
||||||
throwErrorIfInvalidForward(stanza);
|
throwErrorIfInvalidForward(stanza);
|
||||||
|
|
||||||
let to_jid = stanza.getAttribute('to');
|
let to_jid = stanza.getAttribute('to');
|
||||||
@ -178,7 +178,7 @@ export async function parseMessage (stanza, _converse) {
|
|||||||
'thread': stanza.querySelector('thread')?.textContent,
|
'thread': stanza.querySelector('thread')?.textContent,
|
||||||
'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : now,
|
'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : now,
|
||||||
'to': stanza.getAttribute('to'),
|
'to': stanza.getAttribute('to'),
|
||||||
'type': stanza.getAttribute('type')
|
'type': stanza.getAttribute('type') || 'normal'
|
||||||
},
|
},
|
||||||
getErrorAttributes(stanza),
|
getErrorAttributes(stanza),
|
||||||
getOutOfBandAttributes(stanza),
|
getOutOfBandAttributes(stanza),
|
||||||
@ -215,8 +215,8 @@ export async function parseMessage (stanza, _converse) {
|
|||||||
*/
|
*/
|
||||||
attrs = await api.hook('parseMessage', stanza, attrs);
|
attrs = await api.hook('parseMessage', stanza, attrs);
|
||||||
|
|
||||||
// We call this after the hook, to allow plugins to decrypt encrypted
|
// We call this after the hook, to allow plugins (like omemo) to decrypt encrypted
|
||||||
// messages, since we need to parse the message text to determine whether
|
// messages, since we need to parse the message text to determine whether
|
||||||
// there are media urls.
|
// there are media urls.
|
||||||
return Object.assign(attrs, getMediaURLs(attrs.is_encrypted ? attrs.plaintext : attrs.body));
|
return Object.assign(attrs, getMediaURLsMetadata(attrs.is_encrypted ? attrs.plaintext : attrs.body));
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { _converse, api, converse } from '@converse/headless/core.js';
|
|
||||||
import { isServerMessage, } from '@converse/headless/shared/parsers';
|
|
||||||
import { parseMessage } from './parsers.js';
|
|
||||||
import log from '@converse/headless/log.js';
|
import log from '@converse/headless/log.js';
|
||||||
|
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||||
|
import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
|
||||||
|
import { parseMessage } from './parsers.js';
|
||||||
|
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
||||||
|
|
||||||
const { Strophe, sizzle, u } = converse.env;
|
const { Strophe, u } = converse.env;
|
||||||
|
|
||||||
export function openChat (jid) {
|
export function openChat (jid) {
|
||||||
if (!u.isValidJID(jid)) {
|
if (!u.isValidJID(jid)) {
|
||||||
@ -13,7 +14,7 @@ export function openChat (jid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function onClearSession () {
|
export async function onClearSession () {
|
||||||
if (_converse.shouldClearCache()) {
|
if (shouldClearCache()) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
|
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
|
||||||
);
|
);
|
||||||
@ -28,7 +29,7 @@ async function handleErrorMessage (stanza) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const chatbox = await api.chatboxes.get(from_jid);
|
const chatbox = await api.chatboxes.get(from_jid);
|
||||||
if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
if (chatbox?.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||||
chatbox?.handleErrorMessageStanza(stanza);
|
chatbox?.handleErrorMessageStanza(stanza);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,43 +61,22 @@ export function autoJoinChats () {
|
|||||||
export function registerMessageHandlers () {
|
export function registerMessageHandlers () {
|
||||||
_converse.connection.addHandler(
|
_converse.connection.addHandler(
|
||||||
stanza => {
|
stanza => {
|
||||||
if (sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop()) {
|
if (
|
||||||
// MAM messages are handled in converse-mam.
|
['groupchat', 'error'].includes(stanza.getAttribute('type')) ||
|
||||||
// We shouldn't get MAM messages here because
|
isHeadline(stanza) ||
|
||||||
// they shouldn't have a `type` attribute.
|
isServerMessage(stanza) ||
|
||||||
log.warn(`Received a MAM message with type "chat".`);
|
isArchived(stanza)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_converse.handleMessageStanza(stanza);
|
return _converse.handleMessageStanza(stanza) || true;
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
'message',
|
'message',
|
||||||
'chat'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
_converse.connection.addHandler(
|
_converse.connection.addHandler(
|
||||||
stanza => {
|
stanza => handleErrorMessage(stanza) || true,
|
||||||
// Message receipts are usually without the `type` attribute. See #1353
|
|
||||||
if (stanza.getAttribute('type') !== null) {
|
|
||||||
// TODO: currently Strophe has no way to register a handler
|
|
||||||
// for stanzas without a `type` attribute.
|
|
||||||
// We could update it to accept null to mean no attribute,
|
|
||||||
// but that would be a backward-incompatible change
|
|
||||||
return true; // Gets handled above.
|
|
||||||
}
|
|
||||||
_converse.handleMessageStanza(stanza);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Strophe.NS.RECEIPTS,
|
|
||||||
'message'
|
|
||||||
);
|
|
||||||
|
|
||||||
_converse.connection.addHandler(
|
|
||||||
stanza => {
|
|
||||||
handleErrorMessage(stanza);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
null,
|
null,
|
||||||
'message',
|
'message',
|
||||||
'error'
|
'error'
|
||||||
@ -106,28 +86,35 @@ export function registerMessageHandlers () {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler method for all incoming single-user chat "message" stanzas.
|
* Handler method for all incoming single-user chat "message" stanzas.
|
||||||
* @private
|
|
||||||
* @param { MessageAttributes } attrs - The message attributes
|
* @param { MessageAttributes } attrs - The message attributes
|
||||||
*/
|
*/
|
||||||
export async function handleMessageStanza (stanza) {
|
export async function handleMessageStanza (stanza) {
|
||||||
|
stanza = stanza.tree?.() ?? stanza;
|
||||||
|
|
||||||
if (isServerMessage(stanza)) {
|
if (isServerMessage(stanza)) {
|
||||||
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
|
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
|
||||||
const from = stanza.getAttribute('from');
|
const from = stanza.getAttribute('from');
|
||||||
return log.info(`handleMessageStanza: Ignoring incoming server message from JID: ${from}`);
|
return log.info(`handleMessageStanza: Ignoring incoming server message from JID: ${from}`);
|
||||||
}
|
}
|
||||||
const attrs = await parseMessage(stanza, _converse);
|
let attrs;
|
||||||
|
try {
|
||||||
|
attrs = await parseMessage(stanza);
|
||||||
|
} catch (e) {
|
||||||
|
return log.error(e);
|
||||||
|
}
|
||||||
if (u.isErrorObject(attrs)) {
|
if (u.isErrorObject(attrs)) {
|
||||||
attrs.stanza && log.error(attrs.stanza);
|
attrs.stanza && log.error(attrs.stanza);
|
||||||
return log.error(attrs.message);
|
return log.error(attrs.message);
|
||||||
}
|
}
|
||||||
const has_body = !!sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length;
|
// XXX: Need to take XEP-428 <fallback> into consideration
|
||||||
|
const has_body = !!(attrs.body || attrs.plaintext)
|
||||||
const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body);
|
const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body);
|
||||||
await chatbox?.queueMessage(attrs);
|
await chatbox?.queueMessage(attrs);
|
||||||
/**
|
/**
|
||||||
* @typedef { Object } MessageData
|
* @typedef { Object } MessageData
|
||||||
* An object containing the original message stanza, as well as the
|
* An object containing the original message stanza, as well as the
|
||||||
* parsed attributes.
|
* parsed attributes.
|
||||||
* @property { XMLElement } stanza
|
* @property { Element } stanza
|
||||||
* @property { MessageAttributes } stanza
|
* @property { MessageAttributes } stanza
|
||||||
* @property { ChatBox } chatbox
|
* @property { ChatBox } chatbox
|
||||||
*/
|
*/
|
||||||
@ -140,3 +127,33 @@ export async function handleMessageStanza (stanza) {
|
|||||||
*/
|
*/
|
||||||
api.trigger('message', data);
|
api.trigger('message', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the XMPP server to enable Message Carbons
|
||||||
|
* See [XEP-0280](https://xmpp.org/extensions/xep-0280.html#enabling)
|
||||||
|
* @param { Boolean } reconnecting
|
||||||
|
*/
|
||||||
|
export async function enableCarbons () {
|
||||||
|
const domain = Strophe.getDomainFromJid(_converse.bare_jid);
|
||||||
|
const supported = await api.disco.supports(Strophe.NS.CARBONS, domain);
|
||||||
|
|
||||||
|
if (!supported) {
|
||||||
|
log.warn("Not enabling carbons because it's not supported!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iq = new Strophe.Builder('iq', {
|
||||||
|
'from': _converse.connection.jid,
|
||||||
|
'type': 'set'
|
||||||
|
}).c('enable', {xmlns: Strophe.NS.CARBONS});
|
||||||
|
|
||||||
|
const result = await api.sendIQ(iq, null, false);
|
||||||
|
if (result === null) {
|
||||||
|
log.warn(`A timeout occurred while trying to enable carbons`);
|
||||||
|
} else if (u.isErrorStanza(result)) {
|
||||||
|
log.warn('An error occurred while trying to enable message carbons.');
|
||||||
|
log.error(result);
|
||||||
|
} else {
|
||||||
|
log.debug('Message carbons have been enabled.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ const ChatBoxes = Collection.extend({
|
|||||||
* @event _converse#chatBoxesFetched
|
* @event _converse#chatBoxesFetched
|
||||||
* @type { object }
|
* @type { object }
|
||||||
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
|
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
|
||||||
* @property { XMLElement } stanza
|
* @property { Element } stanza
|
||||||
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
|
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
|
||||||
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
|
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2020, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import "../emoji/index.js";
|
import "../emoji/index.js";
|
||||||
|
@ -24,8 +24,8 @@ export default {
|
|||||||
stream: {
|
stream: {
|
||||||
/**
|
/**
|
||||||
* @method api.disco.stream.getFeature
|
* @method api.disco.stream.getFeature
|
||||||
* @param {String} name The feature name
|
* @param { String } name The feature name
|
||||||
* @param {String} xmlns The XML namespace
|
* @param { String } xmlns The XML namespace
|
||||||
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
|
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
|
||||||
*/
|
*/
|
||||||
async getFeature (name, xmlns) {
|
async getFeature (name, xmlns) {
|
||||||
@ -57,10 +57,10 @@ export default {
|
|||||||
* Lets you add new identities for this client (i.e. instance of Converse)
|
* Lets you add new identities for this client (i.e. instance of Converse)
|
||||||
* @method api.disco.own.identities.add
|
* @method api.disco.own.identities.add
|
||||||
*
|
*
|
||||||
* @param {String} category - server, client, gateway, directory, etc.
|
* @param { String } category - server, client, gateway, directory, etc.
|
||||||
* @param {String} type - phone, pc, web, etc.
|
* @param { String } type - phone, pc, web, etc.
|
||||||
* @param {String} name - "Converse"
|
* @param { String } name - "Converse"
|
||||||
* @param {String} lang - en, el, de, etc.
|
* @param { String } lang - en, el, de, etc.
|
||||||
*
|
*
|
||||||
* @example _converse.api.disco.own.identities.clear();
|
* @example _converse.api.disco.own.identities.clear();
|
||||||
*/
|
*/
|
||||||
@ -102,7 +102,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Lets you register new disco features for this client (i.e. instance of Converse)
|
* Lets you register new disco features for this client (i.e. instance of Converse)
|
||||||
* @method api.disco.own.features.add
|
* @method api.disco.own.features.add
|
||||||
* @param {String} name - e.g. http://jabber.org/protocol/caps
|
* @param { String } name - e.g. http://jabber.org/protocol/caps
|
||||||
* @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
|
* @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
|
||||||
*/
|
*/
|
||||||
add (name) {
|
add (name) {
|
||||||
@ -134,8 +134,8 @@ export default {
|
|||||||
* Query for information about an XMPP entity
|
* Query for information about an XMPP entity
|
||||||
*
|
*
|
||||||
* @method api.disco.info
|
* @method api.disco.info
|
||||||
* @param {string} jid The Jabber ID of the entity to query
|
* @param { string } jid The Jabber ID of the entity to query
|
||||||
* @param {string} [node] A specific node identifier associated with the JID
|
* @param { string } [node] A specific node identifier associated with the JID
|
||||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||||
*/
|
*/
|
||||||
info (jid, node) {
|
info (jid, node) {
|
||||||
@ -155,8 +155,8 @@ export default {
|
|||||||
* Query for items associated with an XMPP entity
|
* Query for items associated with an XMPP entity
|
||||||
*
|
*
|
||||||
* @method api.disco.items
|
* @method api.disco.items
|
||||||
* @param {string} jid The Jabber ID of the entity to query for items
|
* @param { string } jid The Jabber ID of the entity to query for items
|
||||||
* @param {string} [node] A specific node identifier associated with the JID
|
* @param { string } [node] A specific node identifier associated with the JID
|
||||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||||
*/
|
*/
|
||||||
items (jid, node) {
|
items (jid, node) {
|
||||||
@ -184,8 +184,8 @@ export default {
|
|||||||
* Get the corresponding `DiscoEntity` instance.
|
* Get the corresponding `DiscoEntity` instance.
|
||||||
*
|
*
|
||||||
* @method api.disco.entities.get
|
* @method api.disco.entities.get
|
||||||
* @param {string} jid The Jabber ID of the entity
|
* @param { string } jid The Jabber ID of the entity
|
||||||
* @param {boolean} [create] Whether the entity should be created if it doesn't exist.
|
* @param { boolean } [create] Whether the entity should be created if it doesn't exist.
|
||||||
* @example _converse.api.disco.entities.get(jid);
|
* @example _converse.api.disco.entities.get(jid);
|
||||||
*/
|
*/
|
||||||
async get (jid, create=false) {
|
async get (jid, create=false) {
|
||||||
@ -193,17 +193,27 @@ export default {
|
|||||||
if (!jid) {
|
if (!jid) {
|
||||||
return _converse.disco_entities;
|
return _converse.disco_entities;
|
||||||
}
|
}
|
||||||
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
if (_converse.disco_entities === undefined) {
|
||||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||||
const msg = `Tried to look up entity ${jid} but _converse.disco_entities has been torn down`;
|
log.warn(`Tried to look up entity ${jid} but _converse.disco_entities has been torn down`);
|
||||||
log.warn(msg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const entity = _converse.disco_entities.get(jid);
|
const entity = _converse.disco_entities.get(jid);
|
||||||
if (entity || !create) {
|
if (entity || !create) {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
return api.disco.entities.create(jid);
|
return api.disco.entities.create({ jid });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return any disco items advertised on this entity
|
||||||
|
*
|
||||||
|
* @method api.disco.entities.items
|
||||||
|
* @param { string } jid The Jabber ID of the entity for which we want to fetch items
|
||||||
|
* @example api.disco.entities.items(jid);
|
||||||
|
*/
|
||||||
|
items (jid) {
|
||||||
|
return _converse.disco_entities.filter(e => e.get('parent_jids')?.includes(jid));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,14 +225,17 @@ export default {
|
|||||||
* `ignore_cache: true` in the options parameter.
|
* `ignore_cache: true` in the options parameter.
|
||||||
*
|
*
|
||||||
* @method api.disco.entities.create
|
* @method api.disco.entities.create
|
||||||
* @param {string} jid The Jabber ID of the entity
|
* @param { object } data
|
||||||
* @param {object} [options] Additional options
|
* @param { string } data.jid - The Jabber ID of the entity
|
||||||
* @param {boolean} [options.ignore_cache]
|
* @param { string } data.parent_jid - The Jabber ID of the parent entity
|
||||||
|
* @param { string } data.name
|
||||||
|
* @param { object } [options] - Additional options
|
||||||
|
* @param { boolean } [options.ignore_cache]
|
||||||
* If true, fetch all features from the XMPP server instead of restoring them from cache
|
* If true, fetch all features from the XMPP server instead of restoring them from cache
|
||||||
* @example _converse.api.disco.entities.create(jid, {'ignore_cache': true});
|
* @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
|
||||||
*/
|
*/
|
||||||
create (jid, options) {
|
create (data, options) {
|
||||||
return _converse.disco_entities.create({'jid': jid}, options);
|
return _converse.disco_entities.create(data, options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -235,11 +248,11 @@ export default {
|
|||||||
* Return a given feature of a disco entity
|
* Return a given feature of a disco entity
|
||||||
*
|
*
|
||||||
* @method api.disco.features.get
|
* @method api.disco.features.get
|
||||||
* @param {string} feature The feature that might be
|
* @param { string } feature The feature that might be
|
||||||
* supported. In the XML stanza, this is the `var`
|
* supported. In the XML stanza, this is the `var`
|
||||||
* attribute of the `<feature>` element. For
|
* attribute of the `<feature>` element. For
|
||||||
* example: `http://jabber.org/protocol/muc`
|
* example: `http://jabber.org/protocol/muc`
|
||||||
* @param {string} jid The JID of the entity
|
* @param { string } jid The JID of the entity
|
||||||
* (and its associated items) which should be queried
|
* (and its associated items) which should be queried
|
||||||
* @returns {promise} A promise which resolves with a list containing
|
* @returns {promise} A promise which resolves with a list containing
|
||||||
* _converse.Entity instances representing the entity
|
* _converse.Entity instances representing the entity
|
||||||
@ -249,22 +262,56 @@ export default {
|
|||||||
* api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
|
* api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
|
||||||
*/
|
*/
|
||||||
async get (feature, jid) {
|
async get (feature, jid) {
|
||||||
if (!jid) {
|
if (!jid) throw new TypeError('You need to provide an entity JID');
|
||||||
throw new TypeError('You need to provide an entity JID');
|
|
||||||
}
|
const entity = await api.disco.entities.get(jid, true);
|
||||||
await api.waitUntil('discoInitialized');
|
|
||||||
let entity = await api.disco.entities.get(jid, true);
|
|
||||||
|
|
||||||
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
||||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||||
const msg = `Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`;
|
log.warn(`Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`);
|
||||||
log.warn(msg);
|
return [];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
entity = await entity.waitUntilFeaturesDiscovered;
|
|
||||||
const promises = [...entity.items.map(i => i.hasFeature(feature)), entity.hasFeature(feature)];
|
const promises = [
|
||||||
|
entity.getFeature(feature),
|
||||||
|
...api.disco.entities.items(jid).map(i => i.getFeature(feature))
|
||||||
|
];
|
||||||
const result = await Promise.all(promises);
|
const result = await Promise.all(promises);
|
||||||
return result.filter(isObject);
|
return result.filter(isObject);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if an entity with the given JID, or if one of its
|
||||||
|
* associated items, supports a given feature.
|
||||||
|
*
|
||||||
|
* @method api.disco.features.has
|
||||||
|
* @param { string } feature The feature that might be
|
||||||
|
* supported. In the XML stanza, this is the `var`
|
||||||
|
* attribute of the `<feature>` element. For
|
||||||
|
* example: `http://jabber.org/protocol/muc`
|
||||||
|
* @param { string } jid The JID of the entity
|
||||||
|
* (and its associated items) which should be queried
|
||||||
|
* @returns {Promise} A promise which resolves with a boolean
|
||||||
|
* @example
|
||||||
|
* api.disco.features.has(Strophe.NS.MAM, _converse.bare_jid);
|
||||||
|
*/
|
||||||
|
async has (feature, jid) {
|
||||||
|
if (!jid) throw new TypeError('You need to provide an entity JID');
|
||||||
|
|
||||||
|
const entity = await api.disco.entities.get(jid, true);
|
||||||
|
|
||||||
|
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
||||||
|
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||||
|
log.warn(`Tried to check if ${jid} supports feature ${feature}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await entity.getFeature(feature)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await Promise.all(api.disco.entities.items(jid).map(i => i.getFeature(feature)));
|
||||||
|
return result.map(isObject).includes(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -272,11 +319,11 @@ export default {
|
|||||||
* Used to determine whether an entity supports a given feature.
|
* Used to determine whether an entity supports a given feature.
|
||||||
*
|
*
|
||||||
* @method api.disco.supports
|
* @method api.disco.supports
|
||||||
* @param {string} feature The feature that might be
|
* @param { string } feature The feature that might be
|
||||||
* supported. In the XML stanza, this is the `var`
|
* supported. In the XML stanza, this is the `var`
|
||||||
* attribute of the `<feature>` element. For
|
* attribute of the `<feature>` element. For
|
||||||
* example: `http://jabber.org/protocol/muc`
|
* example: `http://jabber.org/protocol/muc`
|
||||||
* @param {string} jid The JID of the entity
|
* @param { string } jid The JID of the entity
|
||||||
* (and its associated items) which should be queried
|
* (and its associated items) which should be queried
|
||||||
* @returns {promise} A promise which resolves with `true` or `false`.
|
* @returns {promise} A promise which resolves with `true` or `false`.
|
||||||
* @example
|
* @example
|
||||||
@ -286,16 +333,15 @@ export default {
|
|||||||
* // The feature is not supported
|
* // The feature is not supported
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
async supports (feature, jid) {
|
supports (feature, jid) {
|
||||||
const features = await api.disco.features.get(feature, jid) || [];
|
return api.disco.features.has(feature, jid);
|
||||||
return features.length > 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the features, fields and identities associated with a
|
* Refresh the features, fields and identities associated with a
|
||||||
* disco entity by refetching them from the server
|
* disco entity by refetching them from the server
|
||||||
* @method api.disco.refresh
|
* @method api.disco.refresh
|
||||||
* @param {string} jid The JID of the entity whose features are refreshed.
|
* @param { string } jid The JID of the entity whose features are refreshed.
|
||||||
* @returns {promise} A promise which resolves once the features have been refreshed
|
* @returns {promise} A promise which resolves once the features have been refreshed
|
||||||
* @example
|
* @example
|
||||||
* await api.disco.refresh('room@conference.example.org');
|
* await api.disco.refresh('room@conference.example.org');
|
||||||
@ -316,7 +362,7 @@ export default {
|
|||||||
entity.queryInfo();
|
entity.queryInfo();
|
||||||
} else {
|
} else {
|
||||||
// Create it if it doesn't exist
|
// Create it if it doesn't exist
|
||||||
entity = await api.disco.entities.create(jid, {'ignore_cache': true});
|
entity = await api.disco.entities.create({ jid }, {'ignore_cache': true});
|
||||||
}
|
}
|
||||||
return entity.waitUntilFeaturesDiscovered;
|
return entity.waitUntilFeaturesDiscovered;
|
||||||
},
|
},
|
||||||
@ -333,7 +379,7 @@ export default {
|
|||||||
* Return all the features associated with a disco entity
|
* Return all the features associated with a disco entity
|
||||||
*
|
*
|
||||||
* @method api.disco.getFeatures
|
* @method api.disco.getFeatures
|
||||||
* @param {string} jid The JID of the entity whose features are returned.
|
* @param { string } jid The JID of the entity whose features are returned.
|
||||||
* @returns {promise} A promise which resolves with the returned features
|
* @returns {promise} A promise which resolves with the returned features
|
||||||
* @example
|
* @example
|
||||||
* const features = await api.disco.getFeatures('room@conference.example.org');
|
* const features = await api.disco.getFeatures('room@conference.example.org');
|
||||||
@ -355,7 +401,7 @@ export default {
|
|||||||
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
|
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
|
||||||
*
|
*
|
||||||
* @method api.disco.getFields
|
* @method api.disco.getFields
|
||||||
* @param {string} jid The JID of the entity whose fields are returned.
|
* @param { string } jid The JID of the entity whose fields are returned.
|
||||||
* @example
|
* @example
|
||||||
* const fields = await api.disco.getFields('room@conference.example.org');
|
* const fields = await api.disco.getFields('room@conference.example.org');
|
||||||
*/
|
*/
|
||||||
@ -378,15 +424,15 @@ export default {
|
|||||||
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
|
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
|
||||||
*
|
*
|
||||||
* @method api.disco.getIdentity
|
* @method api.disco.getIdentity
|
||||||
* @param {string} The identity category.
|
* @param { string } The identity category.
|
||||||
* In the XML stanza, this is the `category`
|
* In the XML stanza, this is the `category`
|
||||||
* attribute of the `<identity>` element.
|
* attribute of the `<identity>` element.
|
||||||
* For example: 'pubsub'
|
* For example: 'pubsub'
|
||||||
* @param {string} type The identity type.
|
* @param { string } type The identity type.
|
||||||
* In the XML stanza, this is the `type`
|
* In the XML stanza, this is the `type`
|
||||||
* attribute of the `<identity>` element.
|
* attribute of the `<identity>` element.
|
||||||
* For example: 'pep'
|
* For example: 'pep'
|
||||||
* @param {string} jid The JID of the entity which might have the identity
|
* @param { string } jid The JID of the entity which might have the identity
|
||||||
* @returns {promise} A promise which resolves with a map indicating
|
* @returns {promise} A promise which resolves with a map indicating
|
||||||
* whether an identity with a given type is provided by the entity.
|
* whether an identity with a given type is provided by the entity.
|
||||||
* @example
|
* @example
|
||||||
|
@ -11,7 +11,7 @@ const DiscoEntities = Collection.extend({
|
|||||||
this.fetch({
|
this.fetch({
|
||||||
add: true,
|
add: true,
|
||||||
success: resolve,
|
success: resolve,
|
||||||
error (m, e) {
|
error (_m, e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
reject (new Error("Could not fetch disco entities"));
|
reject (new Error("Could not fetch disco entities"));
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import log from "@converse/headless/log.js";
|
import log from '@converse/headless/log.js';
|
||||||
import sizzle from "sizzle";
|
import sizzle from 'sizzle';
|
||||||
import { Collection } from "@converse/skeletor/src/collection";
|
import { Collection } from '@converse/skeletor/src/collection';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api, converse } from "@converse/headless/core.js";
|
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
@ -19,7 +19,7 @@ const { Strophe } = converse.env;
|
|||||||
const DiscoEntity = Model.extend({
|
const DiscoEntity = Model.extend({
|
||||||
idAttribute: 'jid',
|
idAttribute: 'jid',
|
||||||
|
|
||||||
initialize (attrs, options) {
|
initialize (_, options) {
|
||||||
this.waitUntilFeaturesDiscovered = getOpenPromise();
|
this.waitUntilFeaturesDiscovered = getOpenPromise();
|
||||||
|
|
||||||
this.dataforms = new Collection();
|
this.dataforms = new Collection();
|
||||||
@ -29,22 +29,17 @@ const DiscoEntity = Model.extend({
|
|||||||
this.features = new Collection();
|
this.features = new Collection();
|
||||||
id = `converse.features-${this.get('jid')}`;
|
id = `converse.features-${this.get('jid')}`;
|
||||||
this.features.browserStorage = _converse.createStore(id, 'session');
|
this.features.browserStorage = _converse.createStore(id, 'session');
|
||||||
this.listenTo(this.features, 'add', this.onFeatureAdded)
|
this.listenTo(this.features, 'add', this.onFeatureAdded);
|
||||||
|
|
||||||
this.fields = new Collection();
|
this.fields = new Collection();
|
||||||
id = `converse.fields-${this.get('jid')}`;
|
id = `converse.fields-${this.get('jid')}`;
|
||||||
this.fields.browserStorage = _converse.createStore(id, 'session');
|
this.fields.browserStorage = _converse.createStore(id, 'session');
|
||||||
this.listenTo(this.fields, 'add', this.onFieldAdded)
|
this.listenTo(this.fields, 'add', this.onFieldAdded);
|
||||||
|
|
||||||
this.identities = new Collection();
|
this.identities = new Collection();
|
||||||
id = `converse.identities-${this.get('jid')}`;
|
id = `converse.identities-${this.get('jid')}`;
|
||||||
this.identities.browserStorage = _converse.createStore(id, 'session');
|
this.identities.browserStorage = _converse.createStore(id, 'session');
|
||||||
this.fetchFeatures(options);
|
this.fetchFeatures(options);
|
||||||
|
|
||||||
this.items = new _converse.DiscoEntities();
|
|
||||||
id = `converse.disco-items-${this.get('jid')}`;
|
|
||||||
this.items.browserStorage = _converse.createStore(id, 'session');
|
|
||||||
this.items.fetch();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +54,7 @@ const DiscoEntity = Model.extend({
|
|||||||
await this.waitUntilFeaturesDiscovered;
|
await this.waitUntilFeaturesDiscovered;
|
||||||
return this.identities.findWhere({
|
return this.identities.findWhere({
|
||||||
'category': category,
|
'category': category,
|
||||||
'type': type
|
'type': type,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -67,12 +62,12 @@ const DiscoEntity = Model.extend({
|
|||||||
* Returns a Promise which resolves with a map indicating
|
* Returns a Promise which resolves with a map indicating
|
||||||
* whether a given feature is supported.
|
* whether a given feature is supported.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.DiscoEntity#hasFeature
|
* @method _converse.DiscoEntity#getFeature
|
||||||
* @param { String } feature - The feature that might be supported.
|
* @param { String } feature - The feature that might be supported.
|
||||||
*/
|
*/
|
||||||
async hasFeature (feature) {
|
async getFeature (feature) {
|
||||||
await this.waitUntilFeaturesDiscovered
|
await this.waitUntilFeaturesDiscovered;
|
||||||
if (this.features.findWhere({'var': feature})) {
|
if (this.features.findWhere({ 'var': feature })) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -106,7 +101,7 @@ const DiscoEntity = Model.extend({
|
|||||||
} else {
|
} else {
|
||||||
const store_id = this.features.browserStorage.name;
|
const store_id = this.features.browserStorage.name;
|
||||||
const result = await this.features.browserStorage.store.getItem(store_id);
|
const result = await this.features.browserStorage.store.getItem(store_id);
|
||||||
if (result && result.length === 0 || result === null) {
|
if ((result && result.length === 0) || result === null) {
|
||||||
this.queryInfo();
|
this.queryInfo();
|
||||||
} else {
|
} else {
|
||||||
this.features.fetch({
|
this.features.fetch({
|
||||||
@ -114,9 +109,9 @@ const DiscoEntity = Model.extend({
|
|||||||
success: () => {
|
success: () => {
|
||||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||||
this.trigger('featuresDiscovered');
|
this.trigger('featuresDiscovered');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
this.identities.fetch({add: true});
|
this.identities.fetch({ add: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -135,25 +130,27 @@ const DiscoEntity = Model.extend({
|
|||||||
|
|
||||||
onDiscoItems (stanza) {
|
onDiscoItems (stanza) {
|
||||||
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
|
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
|
||||||
if (item.getAttribute("node")) {
|
if (item.getAttribute('node')) {
|
||||||
// XXX: Ignore nodes for now.
|
// XXX: Ignore nodes for now.
|
||||||
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
|
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const jid = item.getAttribute('jid');
|
const jid = item.getAttribute('jid');
|
||||||
if (this.items.get(jid) === undefined) {
|
|
||||||
const entity = _converse.disco_entities.get(jid);
|
const entity = _converse.disco_entities.get(jid);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
this.items.add(entity);
|
entity.set({ parent_jids: [this.get('jid')] });
|
||||||
} else {
|
} else {
|
||||||
this.items.create({'jid': jid});
|
api.disco.entities.create({
|
||||||
}
|
jid,
|
||||||
|
'parent_jids': [this.get('jid')],
|
||||||
|
'name': item.getAttribute('name'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async queryForItems () {
|
async queryForItems () {
|
||||||
if (this.identities.where({'category': 'server'}).length === 0) {
|
if (this.identities.where({ 'category': 'server' }).length === 0) {
|
||||||
// Don't fetch features and items if this is not a
|
// Don't fetch features and items if this is not a
|
||||||
// server or a conference component.
|
// server or a conference component.
|
||||||
return;
|
return;
|
||||||
@ -162,12 +159,12 @@ const DiscoEntity = Model.extend({
|
|||||||
this.onDiscoItems(stanza);
|
this.onDiscoItems(stanza);
|
||||||
},
|
},
|
||||||
|
|
||||||
onInfo (stanza) {
|
async onInfo (stanza) {
|
||||||
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
|
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
|
||||||
this.identities.create({
|
this.identities.create({
|
||||||
'category': identity.getAttribute('category'),
|
'category': identity.getAttribute('category'),
|
||||||
'type': identity.getAttribute('type'),
|
'type': identity.getAttribute('type'),
|
||||||
'name': identity.getAttribute('name')
|
'name': identity.getAttribute('name'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -176,19 +173,19 @@ const DiscoEntity = Model.extend({
|
|||||||
sizzle('field', form).forEach(field => {
|
sizzle('field', form).forEach(field => {
|
||||||
data[field.getAttribute('var')] = {
|
data[field.getAttribute('var')] = {
|
||||||
'value': field.querySelector('value')?.textContent,
|
'value': field.querySelector('value')?.textContent,
|
||||||
'type': field.getAttribute('type')
|
'type': field.getAttribute('type'),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.dataforms.create(data);
|
this.dataforms.create(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
|
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
|
||||||
this.queryForItems();
|
await this.queryForItems();
|
||||||
}
|
}
|
||||||
Array.from(stanza.querySelectorAll('feature')).forEach(feature => {
|
Array.from(stanza.querySelectorAll('feature')).forEach(feature => {
|
||||||
this.features.create({
|
this.features.create({
|
||||||
'var': feature.getAttribute('var'),
|
'var': feature.getAttribute('var'),
|
||||||
'from': stanza.getAttribute('from')
|
'from': stanza.getAttribute('from'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -197,13 +194,13 @@ const DiscoEntity = Model.extend({
|
|||||||
this.fields.create({
|
this.fields.create({
|
||||||
'var': field.getAttribute('var'),
|
'var': field.getAttribute('var'),
|
||||||
'value': field.querySelector('value')?.textContent,
|
'value': field.querySelector('value')?.textContent,
|
||||||
'from': stanza.getAttribute('from')
|
'from': stanza.getAttribute('from'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||||
this.trigger('featuresDiscovered');
|
this.trigger('featuresDiscovered');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default DiscoEntity;
|
export default DiscoEntity;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
describe("Service Discovery", function () {
|
describe("Service Discovery", function () {
|
||||||
|
|
||||||
describe("Whenever converse.js queries a server for its features", function () {
|
describe("Whenever a server is queried for its features", function () {
|
||||||
|
|
||||||
it("stores the features it receives",
|
it("stores the features it receives",
|
||||||
mock.initConverse(
|
mock.initConverse(
|
||||||
@ -76,23 +76,12 @@ describe("Service Discovery", function () {
|
|||||||
'var': 'jabber:iq:version'});
|
'var': 'jabber:iq:version'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
let entities = await _converse.api.disco.entities.get()
|
|
||||||
expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID
|
|
||||||
expect(entities.get(_converse.domain).features.length).toBe(5);
|
|
||||||
expect(entities.get(_converse.domain).identities.length).toBe(3);
|
|
||||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
|
|
||||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
|
|
||||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
|
|
||||||
expect(entities.get('montague.lit').features.where(
|
|
||||||
{'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
|
|
||||||
expect(entities.get('montague.lit').features.where(
|
|
||||||
{'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
|
|
||||||
|
|
||||||
await u.waitUntil(function () {
|
await u.waitUntil(function () {
|
||||||
// Converse.js sees that the entity has a disco#items feature,
|
// Converse.js sees that the entity has a disco#items feature,
|
||||||
// so it will make a query for it.
|
// so it will make a query for it.
|
||||||
return IQ_stanzas.filter(iq => iq.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]')).length > 0;
|
return IQ_stanzas.filter(iq => iq.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]')).length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* <iq type='result'
|
/* <iq type='result'
|
||||||
* from='catalog.shakespeare.lit'
|
* from='catalog.shakespeare.lit'
|
||||||
* to='romeo@montague.net/orchard'
|
* to='romeo@montague.net/orchard'
|
||||||
@ -119,9 +108,8 @@ describe("Service Discovery", function () {
|
|||||||
* </query>
|
* </query>
|
||||||
* </iq>
|
* </iq>
|
||||||
*/
|
*/
|
||||||
stanza = IQ_stanzas.find(function (iq) {
|
stanza = IQ_stanzas.find(iq => iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]'));
|
||||||
return iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]');
|
|
||||||
});
|
|
||||||
const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
|
const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@ -138,7 +126,6 @@ describe("Service Discovery", function () {
|
|||||||
.c('item', {
|
.c('item', {
|
||||||
'jid': 'words.shakespeare.lit',
|
'jid': 'words.shakespeare.lit',
|
||||||
'name': 'Gateway to Marlowe IM'}).up()
|
'name': 'Gateway to Marlowe IM'}).up()
|
||||||
|
|
||||||
.c('item', {
|
.c('item', {
|
||||||
'jid': 'montague.lit',
|
'jid': 'montague.lit',
|
||||||
'node': 'books',
|
'node': 'books',
|
||||||
@ -151,16 +138,41 @@ describe("Service Discovery", function () {
|
|||||||
'node': 'music',
|
'node': 'music',
|
||||||
'name': 'Music from the time of Shakespeare'
|
'name': 'Music from the time of Shakespeare'
|
||||||
});
|
});
|
||||||
|
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
await u.waitUntil(() => _converse.disco_entities);
|
|
||||||
entities = _converse.disco_entities;
|
const entities = await _converse.api.disco.entities.get()
|
||||||
expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID
|
expect(entities.length).toBe(5); // We have an extra entity, which is the user's JID
|
||||||
expect(entities.get(_converse.domain).items.length).toBe(3);
|
expect(entities.get(_converse.domain).features.length).toBe(5);
|
||||||
expect(entities.get(_converse.domain).items.pluck('jid').includes('people.shakespeare.lit')).toBeTruthy();
|
expect(entities.get(_converse.domain).identities.length).toBe(3);
|
||||||
expect(entities.get(_converse.domain).items.pluck('jid').includes('plays.shakespeare.lit')).toBeTruthy();
|
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
|
||||||
expect(entities.get(_converse.domain).items.pluck('jid').includes('words.shakespeare.lit')).toBeTruthy();
|
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
|
||||||
expect(entities.get(_converse.domain).identities.where({'category': 'conference'}).length).toBe(1);
|
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
|
||||||
expect(entities.get(_converse.domain).identities.where({'category': 'directory'}).length).toBe(1);
|
expect(entities.get('montague.lit').features.where(
|
||||||
|
{'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
|
||||||
|
expect(entities.get('montague.lit').features.where(
|
||||||
|
{'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
|
||||||
|
|
||||||
|
expect(entities.map(e => e.get('jid'))).toEqual([
|
||||||
|
'montague.lit',
|
||||||
|
'romeo@montague.lit',
|
||||||
|
'people.shakespeare.lit',
|
||||||
|
'plays.shakespeare.lit',
|
||||||
|
'words.shakespeare.lit'
|
||||||
|
]);
|
||||||
|
const { api, domain } = _converse;
|
||||||
|
let entity = entities.get(_converse.domain);
|
||||||
|
expect(api.disco.entities.items(domain).length).toBe(3);
|
||||||
|
|
||||||
|
expect(api.disco.entities.items(domain).map(e => e.get('jid'))).toEqual(
|
||||||
|
['people.shakespeare.lit', 'plays.shakespeare.lit', 'words.shakespeare.lit']
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(entity.identities.where({'category': 'conference'}).length).toBe(1);
|
||||||
|
expect(entity.identities.where({'category': 'directory'}).length).toBe(1);
|
||||||
|
|
||||||
|
entity = entities.get('people.shakespeare.lit');
|
||||||
|
expect(entity.get('name')).toBe('Directory of Characters');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ function onDiscoInfoRequest (stanza) {
|
|||||||
if (from !== null) {
|
if (from !== null) {
|
||||||
iqresult.attrs({'to': from});
|
iqresult.attrs({'to': from});
|
||||||
}
|
}
|
||||||
|
|
||||||
iqresult.c('query', attrs);
|
iqresult.c('query', attrs);
|
||||||
_converse.disco._identities.forEach(identity => {
|
_converse.disco._identities.forEach(identity => {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
@ -28,7 +29,7 @@ function onDiscoInfoRequest (stanza) {
|
|||||||
}
|
}
|
||||||
iqresult.c('identity', attrs).up();
|
iqresult.c('identity', attrs).up();
|
||||||
});
|
});
|
||||||
_converse.disco._features.forEach(feature => iqresult.c('feature', {'var': feature}).up());
|
_converse.disco._features.forEach(f => iqresult.c('feature', {'var': f}).up());
|
||||||
api.send(iqresult.tree());
|
api.send(iqresult.tree());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -41,9 +42,7 @@ function addClientFeatures () {
|
|||||||
api.disco.own.features.add(Strophe.NS.CHATSTATES);
|
api.disco.own.features.add(Strophe.NS.CHATSTATES);
|
||||||
api.disco.own.features.add(Strophe.NS.DISCO_INFO);
|
api.disco.own.features.add(Strophe.NS.DISCO_INFO);
|
||||||
api.disco.own.features.add(Strophe.NS.ROSTERX); // Limited support
|
api.disco.own.features.add(Strophe.NS.ROSTERX); // Limited support
|
||||||
if (api.settings.get("message_carbons")) {
|
|
||||||
api.disco.own.features.add(Strophe.NS.CARBONS);
|
api.disco.own.features.add(Strophe.NS.CARBONS);
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Triggered in converse-disco once the core disco features of
|
* Triggered in converse-disco once the core disco features of
|
||||||
* Converse have been added.
|
* Converse have been added.
|
||||||
@ -66,11 +65,11 @@ export async function initializeDisco () {
|
|||||||
_converse.disco_entities = new _converse.DiscoEntities();
|
_converse.disco_entities = new _converse.DiscoEntities();
|
||||||
const id = `converse.disco-entities-${_converse.bare_jid}`;
|
const id = `converse.disco-entities-${_converse.bare_jid}`;
|
||||||
_converse.disco_entities.browserStorage = _converse.createStore(id, 'session');
|
_converse.disco_entities.browserStorage = _converse.createStore(id, 'session');
|
||||||
|
|
||||||
const collection = await _converse.disco_entities.fetchEntities();
|
const collection = await _converse.disco_entities.fetchEntities();
|
||||||
if (collection.length === 0 || !collection.get(_converse.domain)) {
|
if (collection.length === 0 || !collection.get(_converse.domain)) {
|
||||||
// If we don't have an entity for our own XMPP server,
|
// If we don't have an entity for our own XMPP server, create one.
|
||||||
// create one.
|
api.disco.entities.create({'jid': _converse.domain}, {'ignore_cache': true});
|
||||||
_converse.disco_entities.create({'jid': _converse.domain});
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Triggered once the `converse-disco` plugin has been initialized and the
|
* Triggered once the `converse-disco` plugin has been initialized and the
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* @module converse-emoji
|
* @module converse-emoji
|
||||||
* @copyright 2020, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import { ASCII_REPLACE_REGEX, CODEPOINTS_REGEX } from './regexes.js';
|
import './utils.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "../../core.js";
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { html } from 'lit';
|
|
||||||
|
|
||||||
const u = converse.env.utils;
|
|
||||||
|
|
||||||
converse.emojis = {
|
converse.emojis = {
|
||||||
'initialized': false,
|
'initialized': false,
|
||||||
@ -17,200 +15,6 @@ converse.emojis = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const ASCII_LIST = {
|
|
||||||
'*\\0/*':'1f646', '*\\O/*':'1f646', '-___-':'1f611', ':\'-)':'1f602', '\':-)':'1f605', '\':-D':'1f605', '>:-)':'1f606', '\':-(':'1f613',
|
|
||||||
'>:-(':'1f620', ':\'-(':'1f622', 'O:-)':'1f607', '0:-3':'1f607', '0:-)':'1f607', '0;^)':'1f607', 'O;-)':'1f607', '0;-)':'1f607', 'O:-3':'1f607',
|
|
||||||
'-__-':'1f611', ':-Þ':'1f61b', '</3':'1f494', ':\')':'1f602', ':-D':'1f603', '\':)':'1f605', '\'=)':'1f605', '\':D':'1f605', '\'=D':'1f605',
|
|
||||||
'>:)':'1f606', '>;)':'1f606', '>=)':'1f606', ';-)':'1f609', '*-)':'1f609', ';-]':'1f609', ';^)':'1f609', '\':(':'1f613', '\'=(':'1f613',
|
|
||||||
':-*':'1f618', ':^*':'1f618', '>:P':'1f61c', 'X-P':'1f61c', '>:[':'1f61e', ':-(':'1f61e', ':-[':'1f61e', '>:(':'1f620', ':\'(':'1f622',
|
|
||||||
';-(':'1f622', '>.<':'1f623', '#-)':'1f635', '%-)':'1f635', 'X-)':'1f635', '\\0/':'1f646', '\\O/':'1f646', '0:3':'1f607', '0:)':'1f607',
|
|
||||||
'O:)':'1f607', 'O=)':'1f607', 'O:3':'1f607', 'B-)':'1f60e', '8-)':'1f60e', 'B-D':'1f60e', '8-D':'1f60e', '-_-':'1f611', '>:\\':'1f615',
|
|
||||||
'>:/':'1f615', ':-/':'1f615', ':-.':'1f615', ':-P':'1f61b', ':Þ':'1f61b', ':-b':'1f61b', ':-O':'1f62e', 'O_O':'1f62e', '>:O':'1f62e',
|
|
||||||
':-X':'1f636', ':-#':'1f636', ':-)':'1f642', '(y)':'1f44d', '<3':'2764', ':D':'1f603', '=D':'1f603', ';)':'1f609', '*)':'1f609',
|
|
||||||
';]':'1f609', ';D':'1f609', ':*':'1f618', '=*':'1f618', ':(':'1f61e', ':[':'1f61e', '=(':'1f61e', ':@':'1f620', ';(':'1f622', 'D:':'1f628',
|
|
||||||
':$':'1f633', '=$':'1f633', '#)':'1f635', '%)':'1f635', 'X)':'1f635', 'B)':'1f60e', '8)':'1f60e', ':/':'1f615', ':\\':'1f615', '=/':'1f615',
|
|
||||||
'=\\':'1f615', ':L':'1f615', '=L':'1f615', ':P':'1f61b', '=P':'1f61b', ':b':'1f61b', ':O':'1f62e', ':X':'1f636', ':#':'1f636', '=X':'1f636',
|
|
||||||
'=#':'1f636', ':)':'1f642', '=]':'1f642', '=)':'1f642', ':]':'1f642'
|
|
||||||
};
|
|
||||||
|
|
||||||
function toCodePoint(unicode_surrogates) {
|
|
||||||
const r = [];
|
|
||||||
let p = 0;
|
|
||||||
let i = 0;
|
|
||||||
while (i < unicode_surrogates.length) {
|
|
||||||
const c = unicode_surrogates.charCodeAt(i++);
|
|
||||||
if (p) {
|
|
||||||
r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
|
|
||||||
p = 0;
|
|
||||||
} else if (0xD800 <= c && c <= 0xDBFF) {
|
|
||||||
p = c;
|
|
||||||
} else {
|
|
||||||
r.push(c.toString(16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.join('-');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function fromCodePoint (codepoint) {
|
|
||||||
let code = typeof codepoint === 'string' ? parseInt(codepoint, 16) : codepoint;
|
|
||||||
if (code < 0x10000) {
|
|
||||||
return String.fromCharCode(code);
|
|
||||||
}
|
|
||||||
code -= 0x10000;
|
|
||||||
return String.fromCharCode(
|
|
||||||
0xD800 + (code >> 10),
|
|
||||||
0xDC00 + (code & 0x3FF)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function convert (unicode) {
|
|
||||||
// Converts unicode code points and code pairs to their respective characters
|
|
||||||
if (unicode.indexOf("-") > -1) {
|
|
||||||
const parts = [],
|
|
||||||
s = unicode.split('-');
|
|
||||||
for (let i = 0; i < s.length; i++) {
|
|
||||||
let part = parseInt(s[i], 16);
|
|
||||||
if (part >= 0x10000 && part <= 0x10FFFF) {
|
|
||||||
const hi = Math.floor((part - 0x10000) / 0x400) + 0xD800;
|
|
||||||
const lo = ((part - 0x10000) % 0x400) + 0xDC00;
|
|
||||||
part = (String.fromCharCode(hi) + String.fromCharCode(lo));
|
|
||||||
} else {
|
|
||||||
part = String.fromCharCode(part);
|
|
||||||
}
|
|
||||||
parts.push(part);
|
|
||||||
}
|
|
||||||
return parts.join('');
|
|
||||||
}
|
|
||||||
return fromCodePoint(unicode);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unique (arr) {
|
|
||||||
return [...new Set(arr)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTonedEmojis () {
|
|
||||||
if (!converse.emojis.toned) {
|
|
||||||
converse.emojis.toned = unique(
|
|
||||||
Object.values(converse.emojis.json.people)
|
|
||||||
.filter(person => person.sn.includes('_tone'))
|
|
||||||
.map(person => person.sn.replace(/_tone[1-5]/, ''))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return converse.emojis.toned;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function convertASCII2Emoji (str) {
|
|
||||||
// Replace ASCII smileys
|
|
||||||
return str.replace(ASCII_REPLACE_REGEX, (entire, m1, m2, m3) => {
|
|
||||||
if( (typeof m3 === 'undefined') || (m3 === '') || (!(u.unescapeHTML(m3) in ASCII_LIST)) ) {
|
|
||||||
// if the ascii doesnt exist just return the entire match
|
|
||||||
return entire;
|
|
||||||
}
|
|
||||||
m3 = u.unescapeHTML(m3);
|
|
||||||
const unicode = ASCII_LIST[m3].toUpperCase();
|
|
||||||
return m2+convert(unicode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function getEmojiMarkup (data, options={unicode_only: false, add_title_wrapper: false}) {
|
|
||||||
const emoji = data.emoji;
|
|
||||||
const shortname = data.shortname;
|
|
||||||
if (emoji) {
|
|
||||||
if (options.unicode_only) {
|
|
||||||
return emoji;
|
|
||||||
} else if (api.settings.get('use_system_emojis')) {
|
|
||||||
if (options.add_title_wrapper) {
|
|
||||||
return shortname ? html`<span title="${shortname}">${emoji}</span>` : emoji;
|
|
||||||
} else {
|
|
||||||
return emoji;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const path = api.settings.get('emoji_image_path');
|
|
||||||
return html`<img class="emoji"
|
|
||||||
draggable="false"
|
|
||||||
title="${shortname}"
|
|
||||||
alt="${emoji}"
|
|
||||||
src="${path}/72x72/${data.cp}.png"/>`;
|
|
||||||
}
|
|
||||||
} else if (options.unicode_only) {
|
|
||||||
return shortname;
|
|
||||||
} else {
|
|
||||||
return html`<img class="emoji"
|
|
||||||
draggable="false"
|
|
||||||
title="${shortname}"
|
|
||||||
alt="${shortname}"
|
|
||||||
src="${converse.emojis.by_sn[shortname].url}">`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function getShortnameReferences (text) {
|
|
||||||
if (!converse.emojis.initialized) {
|
|
||||||
throw new Error(
|
|
||||||
'getShortnameReferences called before emojis are initialized. '+
|
|
||||||
'To avoid this problem, first await the converse.emojis.initilaized_promise.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const references = [...text.matchAll(converse.emojis.shortnames_regex)].filter(ref => ref[0].length > 0);
|
|
||||||
return references.map(ref => {
|
|
||||||
const cp = converse.emojis.by_sn[ref[0]].cp;
|
|
||||||
return {
|
|
||||||
cp,
|
|
||||||
'begin': ref.index,
|
|
||||||
'end': ref.index+ref[0].length,
|
|
||||||
'shortname': ref[0],
|
|
||||||
'emoji': cp ? convert(cp) : null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function parseStringForEmojis(str, callback) {
|
|
||||||
const UFE0Fg = /\uFE0F/g;
|
|
||||||
const U200D = String.fromCharCode(0x200D);
|
|
||||||
return String(str).replace(CODEPOINTS_REGEX, (emoji, _, offset) => {
|
|
||||||
const icon_id = toCodePoint(emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0Fg, '') : emoji);
|
|
||||||
if (icon_id) callback(icon_id, emoji, offset);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function getCodePointReferences (text) {
|
|
||||||
const references = [];
|
|
||||||
parseStringForEmojis(text, (icon_id, emoji, offset) => {
|
|
||||||
references.push({
|
|
||||||
'begin': offset,
|
|
||||||
'cp': icon_id,
|
|
||||||
'emoji': emoji,
|
|
||||||
'end': offset + emoji.length,
|
|
||||||
'shortname': u.getEmojisByAtrribute('cp')[icon_id]?.sn || ''
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return references;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function addEmojisMarkup (text, options) {
|
|
||||||
let list = [text];
|
|
||||||
[...getShortnameReferences(text), ...getCodePointReferences(text)]
|
|
||||||
.sort((a, b) => b.begin - a.begin)
|
|
||||||
.forEach(ref => {
|
|
||||||
const text = list.shift();
|
|
||||||
const emoji = getEmojiMarkup(ref, options);
|
|
||||||
if (typeof emoji === 'string') {
|
|
||||||
list = [text.slice(0, ref.begin) + emoji + text.slice(ref.end), ...list];
|
|
||||||
} else {
|
|
||||||
list = [text.slice(0, ref.begin), emoji, text.slice(ref.end), ...list];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-emoji', {
|
converse.plugins.add('converse-emoji', {
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
@ -253,7 +57,6 @@ converse.plugins.add('converse-emoji', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for storing data related to the Emoji picker widget
|
* Model for storing data related to the Emoji picker widget
|
||||||
* @class
|
* @class
|
||||||
@ -268,98 +71,6 @@ converse.plugins.add('converse-emoji', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/************************ BEGIN Utils ************************/
|
|
||||||
// Closured cache
|
|
||||||
const emojis_by_attribute = {};
|
|
||||||
|
|
||||||
Object.assign(u, {
|
|
||||||
/**
|
|
||||||
* Returns an emoji represented by the passed in shortname.
|
|
||||||
* Scans the passed in text for shortnames and replaces them with
|
|
||||||
* emoji unicode glyphs or alternatively if it's a custom emoji
|
|
||||||
* without unicode representation then a lit TemplateResult
|
|
||||||
* which represents image tag markup is returned.
|
|
||||||
*
|
|
||||||
* The shortname needs to be defined in `emojis.json`
|
|
||||||
* and needs to have either a `cp` attribute for the codepoint, or
|
|
||||||
* an `url` attribute which points to the source for the image.
|
|
||||||
*
|
|
||||||
* @method u.shortnamesToEmojis
|
|
||||||
* @param { String } str - String containg the shortname(s)
|
|
||||||
* @param { Object } options
|
|
||||||
* @param { Boolean } options.unicode_only - Whether emojis are rendered as
|
|
||||||
* unicode codepoints. If so, the returned result will be an array
|
|
||||||
* with containing one string, because the emojis themselves will
|
|
||||||
* also be strings. If set to false, emojis will be represented by
|
|
||||||
* lit TemplateResult objects.
|
|
||||||
* @param { Boolean } options.add_title_wrapper - Whether unicode
|
|
||||||
* codepoints should be wrapped with a `<span>` element with a
|
|
||||||
* title, so that the shortname is shown upon hovering with the
|
|
||||||
* mouse.
|
|
||||||
* @returns {Array} An array of at least one string, or otherwise
|
|
||||||
* strings and lit TemplateResult objects.
|
|
||||||
*/
|
|
||||||
shortnamesToEmojis (str, options={unicode_only: false, add_title_wrapper: false}) {
|
|
||||||
str = convertASCII2Emoji(str);
|
|
||||||
return addEmojisMarkup(str, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces all shortnames in the passed in string with their
|
|
||||||
* unicode (emoji) representation.
|
|
||||||
* @method u.shortnamesToUnicode
|
|
||||||
* @param { String } str - String containing the shortname(s)
|
|
||||||
* @returns { String }
|
|
||||||
*/
|
|
||||||
shortnamesToUnicode (str) {
|
|
||||||
return u.shortnamesToEmojis(str, {'unicode_only': true})[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the passed in string is just a single emoji shortname;
|
|
||||||
* @method u.isOnlyEmojis
|
|
||||||
* @param { String } shortname - A string which migh be just an emoji shortname
|
|
||||||
* @returns { Boolean }
|
|
||||||
*/
|
|
||||||
isOnlyEmojis (text) {
|
|
||||||
const words = text.trim().split(/\s+/);
|
|
||||||
if (words.length === 0 || words.length > 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const emojis = words.filter(text => {
|
|
||||||
const refs = getCodePointReferences(u.shortnamesToUnicode(text));
|
|
||||||
return refs.length === 1 && (text === refs[0]['shortname'] || text === refs[0]['emoji']);
|
|
||||||
});
|
|
||||||
return emojis.length === words.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @method u.getEmojisByAtrribute
|
|
||||||
* @param { String } attr - The attribute according to which the
|
|
||||||
* returned map should be keyed.
|
|
||||||
* @returns { Object } - Map of emojis with the passed in attribute values
|
|
||||||
* as keys and a list of emojis for a particular category as values.
|
|
||||||
*/
|
|
||||||
getEmojisByAtrribute (attr) {
|
|
||||||
if (emojis_by_attribute[attr]) {
|
|
||||||
return emojis_by_attribute[attr];
|
|
||||||
}
|
|
||||||
if (attr === 'category') {
|
|
||||||
return converse.emojis.json;
|
|
||||||
}
|
|
||||||
const all_variants = converse.emojis.list
|
|
||||||
.map(e => e[attr])
|
|
||||||
.filter((c, i, arr) => arr.indexOf(c) == i);
|
|
||||||
|
|
||||||
emojis_by_attribute[attr] = {};
|
|
||||||
all_variants.forEach(v => (emojis_by_attribute[attr][v] = converse.emojis.list.find(i => i[attr] === v)));
|
|
||||||
return emojis_by_attribute[attr];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/************************ END Utils ************************/
|
|
||||||
|
|
||||||
|
|
||||||
/************************ BEGIN API ************************/
|
|
||||||
// We extend the default converse.js API to add methods specific to MUC groupchats.
|
// We extend the default converse.js API to add methods specific to MUC groupchats.
|
||||||
Object.assign(api, {
|
Object.assign(api, {
|
||||||
/**
|
/**
|
||||||
@ -375,15 +86,14 @@ converse.plugins.add('converse-emoji', {
|
|||||||
async initialize () {
|
async initialize () {
|
||||||
if (!converse.emojis.initialized) {
|
if (!converse.emojis.initialized) {
|
||||||
converse.emojis.initialized = true;
|
converse.emojis.initialized = true;
|
||||||
const { default: json } = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
const module = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
||||||
converse.emojis.json = json;
|
const json = converse.emojis.json = module.default;
|
||||||
converse.emojis.by_sn = Object.keys(json).reduce((result, cat) => Object.assign(result, json[cat]), {});
|
converse.emojis.by_sn = Object.keys(json).reduce((result, cat) => Object.assign(result, json[cat]), {});
|
||||||
converse.emojis.list = Object.values(converse.emojis.by_sn);
|
converse.emojis.list = Object.values(converse.emojis.by_sn);
|
||||||
converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
|
converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
|
||||||
converse.emojis.shortnames = converse.emojis.list.map(m => m.sn);
|
converse.emojis.shortnames = converse.emojis.list.map(m => m.sn);
|
||||||
const getShortNames = () => converse.emojis.shortnames.map(s => s.replace(/[+]/g, "\\$&")).join('|');
|
const getShortNames = () => converse.emojis.shortnames.map(s => s.replace(/[+]/g, "\\$&")).join('|');
|
||||||
converse.emojis.shortnames_regex = new RegExp(getShortNames(), "gi");
|
converse.emojis.shortnames_regex = new RegExp(getShortNames(), "gi");
|
||||||
converse.emojis.toned = getTonedEmojis();
|
|
||||||
converse.emojis.initialized_promise.resolve();
|
converse.emojis.initialized_promise.resolve();
|
||||||
}
|
}
|
||||||
return converse.emojis.initialized_promise;
|
return converse.emojis.initialized_promise;
|
||||||
|
209
src/headless/plugins/emoji/utils.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import { ASCII_REPLACE_REGEX, CODEPOINTS_REGEX } from './regexes.js';
|
||||||
|
import { converse } from "../../core.js";
|
||||||
|
|
||||||
|
const { u } = converse.env;
|
||||||
|
|
||||||
|
// Closured cache
|
||||||
|
const emojis_by_attribute = {};
|
||||||
|
|
||||||
|
|
||||||
|
const ASCII_LIST = {
|
||||||
|
'*\\0/*':'1f646', '*\\O/*':'1f646', '-___-':'1f611', ':\'-)':'1f602', '\':-)':'1f605', '\':-D':'1f605', '>:-)':'1f606', '\':-(':'1f613',
|
||||||
|
'>:-(':'1f620', ':\'-(':'1f622', 'O:-)':'1f607', '0:-3':'1f607', '0:-)':'1f607', '0;^)':'1f607', 'O;-)':'1f607', '0;-)':'1f607', 'O:-3':'1f607',
|
||||||
|
'-__-':'1f611', ':-Þ':'1f61b', '</3':'1f494', ':\')':'1f602', ':-D':'1f603', '\':)':'1f605', '\'=)':'1f605', '\':D':'1f605', '\'=D':'1f605',
|
||||||
|
'>:)':'1f606', '>;)':'1f606', '>=)':'1f606', ';-)':'1f609', '*-)':'1f609', ';-]':'1f609', ';^)':'1f609', '\':(':'1f613', '\'=(':'1f613',
|
||||||
|
':-*':'1f618', ':^*':'1f618', '>:P':'1f61c', 'X-P':'1f61c', '>:[':'1f61e', ':-(':'1f61e', ':-[':'1f61e', '>:(':'1f620', ':\'(':'1f622',
|
||||||
|
';-(':'1f622', '>.<':'1f623', '#-)':'1f635', '%-)':'1f635', 'X-)':'1f635', '\\0/':'1f646', '\\O/':'1f646', '0:3':'1f607', '0:)':'1f607',
|
||||||
|
'O:)':'1f607', 'O=)':'1f607', 'O:3':'1f607', 'B-)':'1f60e', '8-)':'1f60e', 'B-D':'1f60e', '8-D':'1f60e', '-_-':'1f611', '>:\\':'1f615',
|
||||||
|
'>:/':'1f615', ':-/':'1f615', ':-.':'1f615', ':-P':'1f61b', ':Þ':'1f61b', ':-b':'1f61b', ':-O':'1f62e', 'O_O':'1f62e', '>:O':'1f62e',
|
||||||
|
':-X':'1f636', ':-#':'1f636', ':-)':'1f642', '(y)':'1f44d', '<3':'2764', ':D':'1f603', '=D':'1f603', ';)':'1f609', '*)':'1f609',
|
||||||
|
';]':'1f609', ';D':'1f609', ':*':'1f618', '=*':'1f618', ':(':'1f61e', ':[':'1f61e', '=(':'1f61e', ':@':'1f620', ';(':'1f622', 'D:':'1f628',
|
||||||
|
':$':'1f633', '=$':'1f633', '#)':'1f635', '%)':'1f635', 'X)':'1f635', 'B)':'1f60e', '8)':'1f60e', ':/':'1f615', ':\\':'1f615', '=/':'1f615',
|
||||||
|
'=\\':'1f615', ':L':'1f615', '=L':'1f615', ':P':'1f61b', '=P':'1f61b', ':b':'1f61b', ':O':'1f62e', ':X':'1f636', ':#':'1f636', '=X':'1f636',
|
||||||
|
'=#':'1f636', ':)':'1f642', '=]':'1f642', '=)':'1f642', ':]':'1f642'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function toCodePoint(unicode_surrogates) {
|
||||||
|
const r = [];
|
||||||
|
let p = 0;
|
||||||
|
let i = 0;
|
||||||
|
while (i < unicode_surrogates.length) {
|
||||||
|
const c = unicode_surrogates.charCodeAt(i++);
|
||||||
|
if (p) {
|
||||||
|
r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
|
||||||
|
p = 0;
|
||||||
|
} else if (0xD800 <= c && c <= 0xDBFF) {
|
||||||
|
p = c;
|
||||||
|
} else {
|
||||||
|
r.push(c.toString(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.join('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function fromCodePoint (codepoint) {
|
||||||
|
let code = typeof codepoint === 'string' ? parseInt(codepoint, 16) : codepoint;
|
||||||
|
if (code < 0x10000) {
|
||||||
|
return String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
code -= 0x10000;
|
||||||
|
return String.fromCharCode(
|
||||||
|
0xD800 + (code >> 10),
|
||||||
|
0xDC00 + (code & 0x3FF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function convert (unicode) {
|
||||||
|
// Converts unicode code points and code pairs to their respective characters
|
||||||
|
if (unicode.indexOf("-") > -1) {
|
||||||
|
const parts = [],
|
||||||
|
s = unicode.split('-');
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
let part = parseInt(s[i], 16);
|
||||||
|
if (part >= 0x10000 && part <= 0x10FFFF) {
|
||||||
|
const hi = Math.floor((part - 0x10000) / 0x400) + 0xD800;
|
||||||
|
const lo = ((part - 0x10000) % 0x400) + 0xDC00;
|
||||||
|
part = (String.fromCharCode(hi) + String.fromCharCode(lo));
|
||||||
|
} else {
|
||||||
|
part = String.fromCharCode(part);
|
||||||
|
}
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
return parts.join('');
|
||||||
|
}
|
||||||
|
return fromCodePoint(unicode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertASCII2Emoji (str) {
|
||||||
|
// Replace ASCII smileys
|
||||||
|
return str.replace(ASCII_REPLACE_REGEX, (entire, _, m2, m3) => {
|
||||||
|
if( (typeof m3 === 'undefined') || (m3 === '') || (!(u.unescapeHTML(m3) in ASCII_LIST)) ) {
|
||||||
|
// if the ascii doesnt exist just return the entire match
|
||||||
|
return entire;
|
||||||
|
}
|
||||||
|
m3 = u.unescapeHTML(m3);
|
||||||
|
const unicode = ASCII_LIST[m3].toUpperCase();
|
||||||
|
return m2+convert(unicode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShortnameReferences (text) {
|
||||||
|
if (!converse.emojis.initialized) {
|
||||||
|
throw new Error(
|
||||||
|
'getShortnameReferences called before emojis are initialized. '+
|
||||||
|
'To avoid this problem, first await the converse.emojis.initialized_promise'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const references = [...text.matchAll(converse.emojis.shortnames_regex)].filter(ref => ref[0].length > 0);
|
||||||
|
return references.map(ref => {
|
||||||
|
const cp = converse.emojis.by_sn[ref[0]].cp;
|
||||||
|
return {
|
||||||
|
cp,
|
||||||
|
'begin': ref.index,
|
||||||
|
'end': ref.index+ref[0].length,
|
||||||
|
'shortname': ref[0],
|
||||||
|
'emoji': cp ? convert(cp) : null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function parseStringForEmojis(str, callback) {
|
||||||
|
const UFE0Fg = /\uFE0F/g;
|
||||||
|
const U200D = String.fromCharCode(0x200D);
|
||||||
|
return String(str).replace(CODEPOINTS_REGEX, (emoji, _, offset) => {
|
||||||
|
const icon_id = toCodePoint(emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0Fg, '') : emoji);
|
||||||
|
if (icon_id) callback(icon_id, emoji, offset);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getCodePointReferences (text) {
|
||||||
|
const references = [];
|
||||||
|
parseStringForEmojis(text, (icon_id, emoji, offset) => {
|
||||||
|
references.push({
|
||||||
|
'begin': offset,
|
||||||
|
'cp': icon_id,
|
||||||
|
'emoji': emoji,
|
||||||
|
'end': offset + emoji.length,
|
||||||
|
'shortname': getEmojisByAtrribute('cp')[icon_id]?.sn || ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return references;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEmojisMarkup (text) {
|
||||||
|
let list = [text];
|
||||||
|
[...getShortnameReferences(text), ...getCodePointReferences(text)]
|
||||||
|
.sort((a, b) => b.begin - a.begin)
|
||||||
|
.forEach(ref => {
|
||||||
|
const text = list.shift();
|
||||||
|
const emoji = ref.emoji || ref.shortname;
|
||||||
|
list = [text.slice(0, ref.begin) + emoji + text.slice(ref.end), ...list];
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all shortnames in the passed in string with their
|
||||||
|
* unicode (emoji) representation.
|
||||||
|
* @namespace u
|
||||||
|
* @method u.shortnamesToUnicode
|
||||||
|
* @param { String } str - String containing the shortname(s)
|
||||||
|
* @returns { String }
|
||||||
|
*/
|
||||||
|
function shortnamesToUnicode (str) {
|
||||||
|
return addEmojisMarkup(convertASCII2Emoji(str)).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the passed in string is just a single emoji shortname;
|
||||||
|
* @namespace u
|
||||||
|
* @method u.isOnlyEmojis
|
||||||
|
* @param { String } text - A string which migh be just an emoji shortname
|
||||||
|
* @returns { Boolean }
|
||||||
|
*/
|
||||||
|
function isOnlyEmojis (text) {
|
||||||
|
const words = text.trim().split(/\s+/);
|
||||||
|
if (words.length === 0 || words.length > 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const emojis = words.filter(text => {
|
||||||
|
const refs = getCodePointReferences(u.shortnamesToUnicode(text));
|
||||||
|
return refs.length === 1 && (text === refs[0]['shortname'] || text === refs[0]['emoji']);
|
||||||
|
});
|
||||||
|
return emojis.length === words.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace u
|
||||||
|
* @method u.getEmojisByAtrribute
|
||||||
|
* @param { 'category'|'cp'|'sn' } attr
|
||||||
|
* The attribute according to which the returned map should be keyed.
|
||||||
|
* @returns { Object }
|
||||||
|
* Map of emojis with the passed in `attr` used as key and a list of emojis as values.
|
||||||
|
*/
|
||||||
|
function getEmojisByAtrribute (attr) {
|
||||||
|
if (emojis_by_attribute[attr]) {
|
||||||
|
return emojis_by_attribute[attr];
|
||||||
|
}
|
||||||
|
if (attr === 'category') {
|
||||||
|
return converse.emojis.json;
|
||||||
|
}
|
||||||
|
const all_variants = converse.emojis.list
|
||||||
|
.map(e => e[attr])
|
||||||
|
.filter((c, i, arr) => arr.indexOf(c) == i);
|
||||||
|
|
||||||
|
emojis_by_attribute[attr] = {};
|
||||||
|
all_variants.forEach(v => (emojis_by_attribute[attr][v] = converse.emojis.list.find(i => i[attr] === v)));
|
||||||
|
return emojis_by_attribute[attr];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(u, {
|
||||||
|
getEmojisByAtrribute,
|
||||||
|
isOnlyEmojis,
|
||||||
|
shortnamesToUnicode,
|
||||||
|
});
|
@ -1,164 +0,0 @@
|
|||||||
/**
|
|
||||||
* @module converse-headlines
|
|
||||||
* @copyright 2020, the Converse.js contributors
|
|
||||||
* @description XEP-0045 Multi-User Chat Views
|
|
||||||
*/
|
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
|
||||||
import { isHeadline, isServerMessage } from '@converse/headless/shared/parsers';
|
|
||||||
import { parseMessage } from '@converse/headless/plugins/chat/parsers';
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-headlines', {
|
|
||||||
/* Plugin dependencies are other plugins which might be
|
|
||||||
* overridden or relied upon, and therefore need to be loaded before
|
|
||||||
* this plugin.
|
|
||||||
*
|
|
||||||
* If the setting "strict_plugin_dependencies" is set to true,
|
|
||||||
* an error will be raised if the plugin is not found. By default it's
|
|
||||||
* false, which means these plugins are only loaded opportunistically.
|
|
||||||
*
|
|
||||||
* NB: These plugins need to have already been loaded via require.js.
|
|
||||||
*/
|
|
||||||
dependencies: ["converse-chat"],
|
|
||||||
|
|
||||||
overrides: {
|
|
||||||
// Overrides mentioned here will be picked up by converse.js's
|
|
||||||
// plugin architecture they will replace existing methods on the
|
|
||||||
// relevant objects or classes.
|
|
||||||
//
|
|
||||||
// New functions which don't exist yet can also be added.
|
|
||||||
|
|
||||||
ChatBoxes: {
|
|
||||||
model (attrs, options) {
|
|
||||||
const { _converse } = this.__super__;
|
|
||||||
if (attrs.type == _converse.HEADLINES_TYPE) {
|
|
||||||
return new _converse.HeadlinesBox(attrs, options);
|
|
||||||
} else {
|
|
||||||
return this.__super__.model.apply(this, arguments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
/* The initialize function gets called as soon as the plugin is
|
|
||||||
* loaded by converse.js's plugin machinery.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows headline messages
|
|
||||||
* @class
|
|
||||||
* @namespace _converse.HeadlinesBox
|
|
||||||
* @memberOf _converse
|
|
||||||
*/
|
|
||||||
_converse.HeadlinesBox = _converse.ChatBox.extend({
|
|
||||||
defaults () {
|
|
||||||
return {
|
|
||||||
'bookmarked': false,
|
|
||||||
'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")),
|
|
||||||
'message_type': 'headline',
|
|
||||||
'num_unread': 0,
|
|
||||||
'time_opened': this.get('time_opened') || (new Date()).getTime(),
|
|
||||||
'type': _converse.HEADLINES_TYPE
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async initialize () {
|
|
||||||
this.set({'box_id': `box-${this.get('jid')}`});
|
|
||||||
this.initUI();
|
|
||||||
this.initMessages();
|
|
||||||
await this.fetchMessages();
|
|
||||||
/**
|
|
||||||
* Triggered once a {@link _converse.HeadlinesBox} has been created and initialized.
|
|
||||||
* @event _converse#headlinesBoxInitialized
|
|
||||||
* @type { _converse.HeadlinesBox }
|
|
||||||
* @example _converse.api.listen.on('headlinesBoxInitialized', model => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('headlinesBoxInitialized', this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function onHeadlineMessage (stanza) {
|
|
||||||
// Handler method for all incoming messages of type "headline".
|
|
||||||
if (isHeadline(stanza) || isServerMessage(stanza)) {
|
|
||||||
const from_jid = stanza.getAttribute('from');
|
|
||||||
|
|
||||||
await api.waitUntil('rosterInitialized')
|
|
||||||
if (from_jid.includes('@') &&
|
|
||||||
!_converse.roster.get(from_jid) &&
|
|
||||||
!api.settings.get("allow_non_roster_messaging")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stanza.querySelector('body') === null) {
|
|
||||||
// Avoid creating a chat box if we have nothing to show inside it.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const chatbox = _converse.chatboxes.create({
|
|
||||||
'id': from_jid,
|
|
||||||
'jid': from_jid,
|
|
||||||
'type': _converse.HEADLINES_TYPE,
|
|
||||||
'from': from_jid
|
|
||||||
});
|
|
||||||
const attrs = await parseMessage(stanza, _converse);
|
|
||||||
await chatbox.createMessage(attrs);
|
|
||||||
api.trigger('message', {chatbox, stanza, attrs});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************ BEGIN Event Handlers ************************/
|
|
||||||
function registerHeadlineHandler () {
|
|
||||||
_converse.connection.addHandler(message => (onHeadlineMessage(message) || true), null, 'message');
|
|
||||||
}
|
|
||||||
api.listen.on('connected', registerHeadlineHandler);
|
|
||||||
api.listen.on('reconnected', registerHeadlineHandler);
|
|
||||||
/************************ END Event Handlers ************************/
|
|
||||||
|
|
||||||
|
|
||||||
/************************ BEGIN API ************************/
|
|
||||||
Object.assign(api, {
|
|
||||||
/**
|
|
||||||
* The "headlines" namespace, which is used for headline-channels
|
|
||||||
* which are read-only channels containing messages of type
|
|
||||||
* "headline".
|
|
||||||
*
|
|
||||||
* @namespace api.headlines
|
|
||||||
* @memberOf api
|
|
||||||
*/
|
|
||||||
headlines: {
|
|
||||||
/**
|
|
||||||
* Retrieves a headline-channel or all headline-channels.
|
|
||||||
*
|
|
||||||
* @method api.headlines.get
|
|
||||||
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
|
||||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
|
||||||
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
|
||||||
* @returns { Promise<_converse.HeadlinesBox> }
|
|
||||||
*/
|
|
||||||
async get (jids, attrs={}, create=false) {
|
|
||||||
async function _get (jid) {
|
|
||||||
let model = await api.chatboxes.get(jid);
|
|
||||||
if (!model && create) {
|
|
||||||
model = await api.chatboxes.create(jid, attrs, _converse.HeadlinesBox);
|
|
||||||
} else {
|
|
||||||
model = (model && model.get('type') === _converse.HEADLINES_TYPE) ? model : null;
|
|
||||||
if (model && Object.keys(attrs).length) {
|
|
||||||
model.save(attrs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
if (jids === undefined) {
|
|
||||||
const chats = await api.chatboxes.get();
|
|
||||||
return chats.filter(c => (c.get('type') === _converse.HEADLINES_TYPE));
|
|
||||||
} else if (typeof jids === 'string') {
|
|
||||||
return _get(jids);
|
|
||||||
}
|
|
||||||
return Promise.all(jids.map(jid => _get(jid)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/************************ END API ************************/
|
|
||||||
}
|
|
||||||
});
|
|
44
src/headless/plugins/headlines/api.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { _converse, api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* The "headlines" namespace, which is used for headline-channels
|
||||||
|
* which are read-only channels containing messages of type
|
||||||
|
* "headline".
|
||||||
|
*
|
||||||
|
* @namespace api.headlines
|
||||||
|
* @memberOf api
|
||||||
|
*/
|
||||||
|
headlines: {
|
||||||
|
/**
|
||||||
|
* Retrieves a headline-channel or all headline-channels.
|
||||||
|
*
|
||||||
|
* @method api.headlines.get
|
||||||
|
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||||
|
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||||
|
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
|
||||||
|
* @returns { Promise<_converse.HeadlinesFeed> }
|
||||||
|
*/
|
||||||
|
async get (jids, attrs={}, create=false) {
|
||||||
|
async function _get (jid) {
|
||||||
|
let model = await api.chatboxes.get(jid);
|
||||||
|
if (!model && create) {
|
||||||
|
model = await api.chatboxes.create(jid, attrs, _converse.HeadlinesFeed);
|
||||||
|
} else {
|
||||||
|
model = (model && model.get('type') === _converse.HEADLINES_TYPE) ? model : null;
|
||||||
|
if (model && Object.keys(attrs).length) {
|
||||||
|
model.save(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
if (jids === undefined) {
|
||||||
|
const chats = await api.chatboxes.get();
|
||||||
|
return chats.filter(c => (c.get('type') === _converse.HEADLINES_TYPE));
|
||||||
|
} else if (typeof jids === 'string') {
|
||||||
|
return _get(jids);
|
||||||
|
}
|
||||||
|
return Promise.all(jids.map(jid => _get(jid)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
31
src/headless/plugins/headlines/feed.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import ChatBox from '@converse/headless/plugins/chat/model.js';
|
||||||
|
import { _converse, api } from '../../core.js';
|
||||||
|
|
||||||
|
|
||||||
|
export default class HeadlinesFeed extends ChatBox {
|
||||||
|
|
||||||
|
defaults () {
|
||||||
|
return {
|
||||||
|
'bookmarked': false,
|
||||||
|
'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")),
|
||||||
|
'message_type': 'headline',
|
||||||
|
'num_unread': 0,
|
||||||
|
'time_opened': this.get('time_opened') || (new Date()).getTime(),
|
||||||
|
'type': _converse.HEADLINES_TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize () {
|
||||||
|
this.set({'box_id': `box-${this.get('jid')}`});
|
||||||
|
this.initUI();
|
||||||
|
this.initMessages();
|
||||||
|
await this.fetchMessages();
|
||||||
|
/**
|
||||||
|
* Triggered once a { @link _converse.HeadlinesFeed } has been created and initialized.
|
||||||
|
* @event _converse#headlinesFeedInitialized
|
||||||
|
* @type { _converse.HeadlinesFeed }
|
||||||
|
* @example _converse.api.listen.on('headlinesFeedInitialized', model => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('headlinesFeedInitialized', this);
|
||||||
|
}
|
||||||
|
}
|