Compare commits

..

119 Commits

Author SHA1 Message Date
a461444bed make version (instead of make release) 2023-10-08 13:53:45 +00:00
1307f87912 Simplify chapril release process 2023-10-07 08:15:40 +00:00
23cf858a02 Add some bash safeguards in README.charpil.md 2023-10-07 08:13:09 +00:00
b49146b36f [Branding] Customize homepage's text and links + CSS adjustments. 2023-10-07 08:13:09 +00:00
8d67dce865 [Branding] Replace Converse's logo with Chapril's logo. 2023-10-07 08:13:07 +00:00
543807aa50 Create a README for Chapril's build. 2023-10-07 08:12:53 +00:00
JC Brand
82a8c3f9fc Release 10.1.6 2023-08-31 21:30:48 +02:00
JC Brand
3e680e88d8 Fix import path 2023-08-31 21:20:40 +02:00
JC Brand
005f5374f0 Fixes #3246 2023-08-31 21:20:40 +02:00
JC Brand
79bb8e76ce Fix GIF rendering artifacts related to patching 2023-08-31 21:20:40 +02:00
JC Brand
61192f91d9 Fix GIF rendering issue
Remove the GIF parsing code from this repo and instead add a dependency on gifuct-js.
2023-08-31 21:20:40 +02:00
JC Brand
e31d4c7bac Release 10.1.5
Refine the `dist` step by creating a proper temporary file.

I spent a lot of time trying to pinpoint the underlying cause, why
translation chunk files are empty when generating a bundle with newer JS
features (i.e. not pinning preset-env to IE11) but couldn't find it.
2023-06-30 06:38:40 +02:00
JC Brand
c30569dfd3 Release 10.1.5
Found another bug while trying to make the release.

Running `npm run nodeps` last breaks dynamic importing of the DayJS
translations. I'm not sure why, I think maybe because of broken `.map`
files.

The fix for now is to move the js-po files out of the way, and then copy
them back in after running `npm run build`.

Hopefully all of this won't be necessary with gettext 0.22 which should
support template literals
2023-06-29 23:41:15 +02:00
JC Brand
5e02b9bd5d Release 10.1.5 (this time hopefully for real)
As I was making the release, I found a bug in the Makefile that still
prevented the locale JSON files from being properly generated.
2023-06-29 23:09:49 +02:00
JC Brand
9114db8764 Release 10.1.5 2023-06-29 22:29:11 +02:00
JC Brand
bc7621c25d Updates #3207 - Generate po files in the release checkout
Refactor the Makefile somewhat.

- Rename `make release` to `make version`
- Add `make release-checkout` which checks out the release branch
2023-06-29 22:16:19 +02:00
ssantos
ae518aa2c3 Translated using Weblate (Portuguese)
Currently translated at 99.8% (539 of 540 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/pt/
2023-06-29 21:55:15 +02:00
josé m
e4a4b2819c Translated using Weblate (Galician)
Currently translated at 100.0% (540 of 540 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/gl/
2023-06-29 21:55:15 +02:00
JC Brand
5310021b67 Modernize the i18n code.
- Remove old deprecated API methods `systemLanguage`, `browserLanguage`
  and `userLanguage`.
- Add types via JSDoc
2023-06-29 09:43:53 +02:00
SilverYoCha
e18fdd56b1 Fix #3209.
Fixing some technical errors when avoiding importing the `converse` global with bootstrap modal API.
2023-06-27 11:07:35 +02:00
JC Brand
94963662e7 Fix ordering or args 2023-06-25 11:22:16 +02:00
JC Brand
4f14d50f5d Check out depth of 1 when deploying 2023-06-25 11:15:46 +02:00
JC Brand
699ab71f21 Larger sponsor image 2023-06-25 11:13:24 +02:00
JC Brand
daeb641530 Postrelease 2023-06-25 11:09:50 +02:00
JC Brand
6e71555ee4 Release 10.1.4 2023-06-25 11:03:06 +02:00
JC Brand
27d61411ab Update release instructions 2023-06-25 10:58:58 +02:00
JC Brand
3328e1c94c Add libsignal as a 3rd party dependency
Ultimately I'd like to use libomemo.js, but it's not yet ready
https://github.com/conversejs/libomemo.js
2023-06-25 10:58:58 +02:00
Eric
c63c787a6d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (540 of 540 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-06-25 10:58:48 +02:00
gallegonovato
0efeead71a Translated using Weblate (Spanish)
Currently translated at 100.0% (540 of 540 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/es/
2023-06-25 10:58:48 +02:00
JC Brand
5822fade32 Add make recipe to create tar files 2023-06-23 13:34:51 +02:00
JC Brand
621612d126 Postrelease 2023-06-23 10:39:32 +02:00
JC Brand
536350155d Release 10.1.3 2023-06-23 10:17:53 +02:00
Emmanuel Gil Peyrot
841a57fd66 Translated using Weblate (French)
Currently translated at 98.3% (533 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/fr/
2023-06-23 10:09:14 +02:00
Breno Peres
175595098a Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/pt_BR/
2023-06-23 10:09:14 +02:00
Breno Peres
03655b45b2 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/pt_BR/
2023-06-23 10:09:14 +02:00
JC Brand
fa9ae16ead Run npm update 2023-06-23 10:01:34 +02:00
JC Brand
cb1f929045 Fixes #3123: Contacts do not show up online until chat is opened with them.
The issue was that nothing was listening to the new `presenceChanged` event.
2023-06-09 20:37:27 +02:00
JC Brand
9ba339a6d9 Add colon 2023-06-09 17:12:49 +02:00
JC Brand
bc452596a7 Add workflow_dispatch
So that worfklows can be triggered.
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
2023-06-09 17:10:29 +02:00
JC Brand
2b732cdb94 Bump filesize to latest version and fix issues. 2023-06-09 14:55:09 +02:00
JC Brand
1e4937bf41 Run npm update and fix resulting dynamic import error.
For some reason the newer webpack versions can't handle the destructured
assignment from a dynamic import of a JSON file.
2023-06-09 12:34:58 +02:00
JC Brand
8b28ffecfe Don't run type check when making dist files 2023-06-09 11:40:04 +02:00
JC Brand
80bf81db6f Use Node 18.x in CI tests 2023-06-09 11:25:23 +02:00
JC Brand
7fdbcd0e64 Make sure node_modules are installed 2023-06-09 11:23:56 +02:00
JC Brand
975a2c2305 Remove Lerna and bump Node version.
Lerna doesn't seem to be needed anymore, NPM workspaces functionality suffices.
2023-06-09 11:19:23 +02:00
dependabot[bot]
abad4d054b Bump postcss-loader from 7.0.2 to 7.3.2
Bumps [postcss-loader](https://github.com/webpack-contrib/postcss-loader) from 7.0.2 to 7.3.2.
- [Release notes](https://github.com/webpack-contrib/postcss-loader/releases)
- [Changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/postcss-loader/compare/v7.0.2...v7.3.2)

---
updated-dependencies:
- dependency-name: postcss-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-09 10:58:29 +02:00
dependabot[bot]
b5b6d1f8b2 Bump webpack-dev-server from 4.11.1 to 4.15.0
Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 4.11.1 to 4.15.0.
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.11.1...v4.15.0)

---
updated-dependencies:
- dependency-name: webpack-dev-server
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-09 10:25:46 +02:00
dependabot[bot]
64aef7f70d Bump @babel/preset-env from 7.20.2 to 7.22.5
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.20.2 to 7.22.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.22.5/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-09 10:24:59 +02:00
dependabot[bot]
4f0f210e21
Bump @babel/cli from 7.20.7 to 7.22.5 (#3189)
Bumps [@babel/cli](https://github.com/babel/babel/tree/HEAD/packages/babel-cli) from 7.20.7 to 7.22.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.22.5/packages/babel-cli)

---
updated-dependencies:
- dependency-name: "@babel/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-09 10:24:42 +02:00
dependabot[bot]
c4299e92be
Bump karma-jasmine-html-reporter from 2.0.0 to 2.1.0 (#3180)
Bumps [karma-jasmine-html-reporter](https://github.com/dfederm/karma-jasmine-html-reporter) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/dfederm/karma-jasmine-html-reporter/releases)
- [Commits](https://github.com/dfederm/karma-jasmine-html-reporter/commits)

---
updated-dependencies:
- dependency-name: karma-jasmine-html-reporter
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 12:50:13 +02:00
dependabot[bot]
0cc03e7e7a
Bump @typescript-eslint/eslint-plugin from 5.48.0 to 5.59.9 (#3181)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.48.0 to 5.59.9.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.59.9/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 12:49:31 +02:00
dependabot[bot]
f9f246282b
Bump mini-css-extract-plugin from 2.7.2 to 2.7.6 (#3184)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.7.2 to 2.7.6.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.7.2...v2.7.6)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 12:48:48 +02:00
dependabot[bot]
3e4a8b23fe
Bump webpack-cli from 4.10.0 to 5.1.4 (#3185)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.10.0 to 5.1.4.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.10.0...webpack-cli@5.1.4)

---
updated-dependencies:
- dependency-name: webpack-cli
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 12:47:57 +02:00
dependabot[bot]
cdda140137
Bump webpack from 5.75.0 to 5.76.0 (#3173)
Bumps [webpack](https://github.com/webpack/webpack) from 5.75.0 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.75.0...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 14:40:29 +02:00
dependabot[bot]
0a15b003dc
Bump sass from 1.58.1 to 1.62.1 (#3171)
Bumps [sass](https://github.com/sass/dart-sass) from 1.58.1 to 1.62.1.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.58.1...1.62.1)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 12:48:36 +02:00
dependabot[bot]
23539ecf47
Bump socket.io-parser from 4.2.2 to 4.2.4 (#3175)
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.2.2 to 4.2.4.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.2.2...4.2.4)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 12:47:52 +02:00
dependabot[bot]
efa448956e
Bump dayjs from 1.11.6 to 1.11.8 (#3172)
Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.6 to 1.11.8.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.6...v1.11.8)

---
updated-dependencies:
- dependency-name: dayjs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 12:47:13 +02:00
dependabot[bot]
743f336dc5
Bump jsdoc from 4.0.0 to 4.0.2 (#3146)
Bumps [jsdoc](https://github.com/jsdoc/jsdoc) from 4.0.0 to 4.0.2.
- [Release notes](https://github.com/jsdoc/jsdoc/releases)
- [Changelog](https://github.com/jsdoc/jsdoc/blob/4.0.2/CHANGES.md)
- [Commits](https://github.com/jsdoc/jsdoc/compare/4.0.0...4.0.2)

---
updated-dependencies:
- dependency-name: jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 12:46:35 +02:00
dependabot[bot]
8a0386fdcc
Bump @babel/core from 7.20.12 to 7.22.1 (#3170)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.20.12 to 7.22.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.22.1/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 11:41:21 +02:00
JC Brand
b647f5463f Check out new repo when making a release
Ignore media dir
2023-06-06 10:31:53 +02:00
Breno Reis
46f2e40f91
Small pt_BR corrections (#3168)
* 'a bastante tempo' -> 'há bastante tempo'

* Fix a wrong comma usage

* Update CHANGES.md
2023-06-06 09:58:45 +02:00
Daniele Bortoluzzi
8b1b4b6446 Translated using Weblate (Italian)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/it/
2023-06-06 09:48:58 +02:00
Raphaël Maginot
9407d00c67 Translated using Weblate (French)
Currently translated at 97.4% (528 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/fr/
2023-06-06 09:48:58 +02:00
Sergi Font
f0d584aed6 Translated using Weblate (Catalan)
Currently translated at 81.1% (440 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ca/
2023-06-06 09:48:58 +02:00
Meno25
2768c2a3ec Translated using Weblate (Arabic)
Currently translated at 69.3% (376 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ar/
2023-06-06 09:48:58 +02:00
Mr. EddX
8fb1716681 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/bg/
2023-06-06 09:48:58 +02:00
Mr. EddX
507cfa9ef2 Translated using Weblate (Bulgarian)
Currently translated at 33.7% (183 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/bg/
2023-06-06 09:48:58 +02:00
Meno25
5ef7807a68 Translated using Weblate (Arabic)
Currently translated at 69.3% (376 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ar/
2023-06-06 09:48:58 +02:00
Meno25
952d3cf440 Translated using Weblate (Arabic)
Currently translated at 68.6% (372 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ar/
2023-06-06 09:48:58 +02:00
Kim Alvefur
c9a95faa35 Translated using Weblate (Swedish)
Currently translated at 97.4% (528 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/sv/
2023-06-06 09:48:58 +02:00
gnu-ewm
02185cee5f Translated using Weblate (Polish)
Currently translated at 94.6% (513 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/pl/
2023-06-06 09:48:58 +02:00
K.Y
40290f27e7 Translated using Weblate (Uyghur)
Currently translated at 97.9% (531 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ug/
2023-06-06 09:48:58 +02:00
Sam Mendes
bbfcb74eaa Translated using Weblate (Persian)
Currently translated at 22.5% (122 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/fa/
2023-06-06 09:48:58 +02:00
Zoltán Tóth
a2e618a8f6 Translated using Weblate (Hungarian)
Currently translated at 93.5% (507 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/hu/
2023-06-06 09:48:58 +02:00
Ze-oZ
ebbb39a9aa Translated using Weblate (Greek)
Currently translated at 58.4% (317 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/el/
2023-06-06 09:48:58 +02:00
Allan Nordhøy
2d66a974d0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 66.2% (359 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/nb_NO/
2023-06-06 09:48:58 +02:00
nautilusx
fa12dae7bc Translated using Weblate (German)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/de/
2023-06-06 09:48:58 +02:00
Eric
df25e16438 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-06-06 09:48:58 +02:00
Oğuz Ersen
c39d61f62c Translated using Weblate (Turkish)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/tr/
2023-06-06 09:48:58 +02:00
josé m
01ce344d82 Translated using Weblate (Galician)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/gl/
2023-06-06 09:48:58 +02:00
gallegonovato
bb428fa1d7 Translated using Weblate (Spanish)
Currently translated at 100.0% (542 of 542 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/es/
2023-06-06 09:48:58 +02:00
JC Brand
5a710e4601 Add new sponsor BairesDev 2023-06-05 16:53:49 +02:00
JC Brand
f91f4cb7ab Allow arguments to be passed when running tests 2023-06-05 09:39:37 +02:00
JC Brand
f2ac748e06 Add test and lint NPM scripts 2023-06-02 07:31:47 +02:00
JC Brand
27a47c6ee8 fixes #3165
Use configured nickname in profile view in the control box.
Remove doc for the unimplemented `fullname` setting.
2023-06-02 07:24:37 +02:00
JC Brand
0a338feb40 headless.js has been renamed to index.js 2023-03-15 17:53:33 +01:00
JC Brand
d74daccfbb Fix logo transparency and add extra platforms in doap file 2023-03-03 17:28:26 +01:00
JC Brand
5c3acc8a4d Rename package module files to index.js
Makes it easier to import, you don't have to specify the file name, just
the package name.
2023-03-03 17:28:26 +01:00
JC Brand
48e7987f38 Start work on removing constants from _converse object 2023-03-03 17:28:26 +01:00
JC Brand
4d2a8e9f8d Create new config setting stanza_timeout
And move STANZA_TIMEOUT off `_converse` and into constants.js
2023-03-03 17:28:26 +01:00
JC Brand
3d0e9a7374 Move CONNECTION_STATUS to constant.js 2023-03-03 17:28:26 +01:00
JC Brand
fb054c40be Experimenting with avoiding importing the converse global 2023-03-03 17:28:26 +01:00
drlight17
c3c666e568
dragresize over iframe fix function concept (#3157)
Fixes stuttering of conversejs overlayed dragresize UI elements over the iframes on the parent page.
2023-03-03 11:52:24 +01:00
JC Brand
6b365458f5 Get rid of lodash's invoke method 2023-02-26 19:32:55 +01:00
JC Brand
885a3d92b5 Move private API to own file 2023-02-26 18:10:01 +01:00
JC Brand
6df80507f3 Move ConnectionFeedback model to its own file 2023-02-26 18:10:01 +01:00
JC Brand
16754c15aa Register Strophe namespaces in constants.js 2023-02-26 18:10:01 +01:00
JC Brand
70a10efd49 Move public API into its own file 2023-02-26 18:10:01 +01:00
JC Brand
9a63629b40 Get rid of lodash's isFunction 2023-02-26 18:10:01 +01:00
JC Brand
dcfc0d50f4 Refactor shared API functions out into src/headless/shared/api 2023-02-26 18:10:01 +01:00
JC Brand
fde55bea2c Include XEP-0172 nick in all outgoing presence subscribe stanzas 2023-02-26 09:53:25 +01:00
JC Brand
3530ccc35d Add initial TypeScript support and declaration files 2023-02-23 09:38:11 +01:00
JC Brand
05c5cd1046 Remove call to api.confirm in @converse/headless 2023-02-22 15:21:56 +01:00
JC Brand
c38c706079 Fix logo not showing in background 2023-02-22 12:25:21 +01:00
JC Brand
e1e93c2ec9 Properly set checkbox state in bookmark form 2023-02-22 12:25:21 +01:00
JC Brand
16b442ac19 Occupant modal: Only show role/affiliation forms for moderators 2023-02-22 09:32:20 +01:00
JC Brand
0122bd7bab Import emoji utils.
Fixes `TypeError: core_default.isOnlyEmojis is not a function`
when using only `@converse/headless`.
2023-02-22 09:10:22 +01:00
JC Brand
56259fd22e Add the role form to the occupant modal 2023-02-21 23:14:16 +01:00
JC Brand
85181053e6 Make the role form a custom element
So that it can be used in the occupant modal as well.
2023-02-21 23:14:16 +01:00
JC Brand
26062df4e5 Fix logo path 2023-02-21 23:14:16 +01:00
JC Brand
2fb9fec8f8 Use addEventListener in the constructor
In Lit elements, the `initialize` function can get called multiple
times, thereby causing a memory leak if `addEventListener` is called
there and not removed.

The `ElementView` only calls it once, but I'm making the change so that
it's not forgotten when we eventually move to using Lit elements for
modals.
2023-02-21 23:14:16 +01:00
JC Brand
d4cb67dc5f Add affiliation form to the occupant modal 2023-02-21 23:14:16 +01:00
JC Brand
ad7b4ae343 Add affiliation form to the occupant modal 2023-02-21 20:18:59 +01:00
JC Brand
57f489f61b Turn the MUC affiliation form into a component
So that it can be used elsewhere, for example in the occupant modal.
2023-02-21 20:18:16 +01:00
JC Brand
6ce8879e9c CSS: fix MUC textarea color when correcting a message 2023-02-21 17:41:39 +01:00
JC Brand
1ee4cce2fd Modtools: fix wrench icon not visible in dracula theme
Also fix spacing of buttons
2023-02-21 09:30:29 +01:00
JC Brand
b69e5b5482 Create occupants based on messages
That way the occupant modal can still be shown in MUCs even if the user
is no longer online.
2023-02-20 22:02:13 +01:00
JC Brand
3f6ad0d950 CSS build fixes
- Bump postcss.
- Remove postcss-clean. It's incompatible with newer versions and
  apparently unnecesary.
- Finally got CSS source maps to work again
2023-02-20 12:43:20 +01:00
JC Brand
9899351597 Add a deploy Makefile recipe 2023-02-17 12:19:08 +01:00
202 changed files with 71950 additions and 53583 deletions

View File

@ -1,7 +1,7 @@
{ {
"parser": "@babel/eslint-parser", "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,

View File

@ -8,6 +8,7 @@ on:
branches: [ master ] branches: [ master ]
pull_request: pull_request:
branches: [ master ] branches: [ master ]
workflow_dispatch:
jobs: jobs:
build: build:
@ -17,7 +18,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [16.x] node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps: steps:

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
# Distribution directory # Distribution directory
dist dist
# conversejs/media repo checkout
media
# Editor fluff # Editor fluff
*~ *~
.sw? .sw?

2
.nvmrc
View File

@ -1 +1 @@
v16.13.1 v18.15.0

View File

@ -1,5 +1,4 @@
{ {
"arrowParens": "avoid",
"printWidth": 120, "printWidth": 120,
"quoteProps": "preserve", "quoteProps": "preserve",
"singleQuote": true, "singleQuote": true,

36541
3rdparty/libsignal-protocol.js vendored Normal file

File diff suppressed because one or more lines are too long

1
3rdparty/libsignal-protocol.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,35 @@
# 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) ## 10.1.2 (2023-02-17)
- #1490: Busy-loop when fetching registration form fails - #1490: Busy-loop when fetching registration form fails
@ -22,7 +52,7 @@
- #2925: File upload is not always enabled - #2925: File upload is not always enabled
- #3001: Add option to save SCRAM details and to use them to stay logged in upon reload - #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 - Add a "Add to Contacts" button in MUC occupant modals
- Updated tranlsations and new language Uyghur - Updated translations and add support for Uyghur
- New config option [reuse_scram_keys](https://conversejs.org/docs/html/configuration.html#reuse-scram-keys) - New config option [reuse_scram_keys](https://conversejs.org/docs/html/configuration.html#reuse-scram-keys)

View File

@ -2,7 +2,7 @@
* *
* An XMPP chat client that runs in the browser. * An XMPP chat client that runs in the browser.
* *
* Version: 10.1.2 * Version: 10.1.6
* *
* Copyright: JC Brand 2013-2018 * Copyright: JC Brand 2013-2018
* Except for 3rd party dependencies. * Except for 3rd party dependencies.

View File

@ -3,13 +3,11 @@ BOOTSTRAP = ./node_modules/
BUILDDIR = ./docs BUILDDIR = ./docs
KARMA ?= ./node_modules/.bin/karma KARMA ?= ./node_modules/.bin/karma
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
@ -60,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/webpack.common.js webpack/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=10.1.2 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 $$?;
@ -83,10 +82,9 @@ po:
######################################################################## ########################################################################
## Release management ## Release management
.PHONY: release .PHONY: version
release: version:
find ./src -name "*~" -exec rm {} \; $(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/shared/constants.js
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/core.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
@ -101,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
@ -125,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
@ -147,6 +156,9 @@ 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 build npm run build
@ -193,7 +205,16 @@ src/headless/dist/converse-headless.min.js: src webpack/webpack.common.js node_m
dist:: node_modules src/* | dist/website.css dist/website.min.css dist:: node_modules src/* | dist/website.css dist/website.min.css
npm run headless npm run headless
npm run build # 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
@ -202,20 +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
.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

17
README.chapril.md Normal file
View 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?}/
```

View File

@ -167,13 +167,13 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
## Sponsors ## Sponsors
<p> <p>
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"> <a href="https://bairesdev.com/sponsoring-open-source-projects/?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="BairesDev" src="https://raw.githubusercontent.com/conversejs/media/main/logos/bairesdev-primary.png" width="200">
</a> </a>
</p> </p>
<p> <p>
<a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"> <a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.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>
@ -181,3 +181,8 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
<img alt="Prime Sound" src="https://raw.githubusercontent.com/conversejs/media/main/logos/primesound.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>
<a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
</a>
</p>

View File

@ -1,19 +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=10.1.2` 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 10.1.2"` 6. `git commit -am "Release 10.1.6"`
7. `git tag -s v10.1.2 -m "Release 10.1.2"` 7. `git tag -s v10.1.6 -m "Release 10.1.6"`
8. Run `git push && git push origin v10.1.2` 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 v10.1.2 git@github.com:conversejs/converse.js.git 10.1.2`
* `cd 10.1.2 && nvm install && ASSET_PATH=https://cdn.conversejs.org/10.1.2/dist/ make dist && make doc`
* `cd .. && git pull && nvm install && 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 publish && cd src/headless/ && npm publish` * Upload tar files
11. Update https://conversejs.org
* `cd /home/conversejs/converse.js`
* `make deploy VERSION=10.1.6`
12. Update the repository on weblate 12. Update the repository on weblate
13. Decide on next release number and run `make postrelease VERSION=9.1.2` 13. Decide on next release number and run `make postrelease VERSION=10.1.7`

View File

@ -10,6 +10,9 @@
<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>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"/> <schema:logo rdf:resource="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/conversejs-filled.svg"/>
<repository> <repository>
<GitRepository> <GitRepository>

View File

@ -28,6 +28,7 @@
}); });
converse.initialize({ converse.initialize({
i18n: 'af',
theme: 'dracula', theme: 'dracula',
auto_away: 300, auto_away: 300,
enable_smacks: true, enable_smacks: true,
@ -39,10 +40,10 @@
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'],
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' } // connection_options: { worker: '/dist/shared-connection-worker.js' }
}); });
</script> </script>
</body> </body>

View File

@ -2,8 +2,8 @@
<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> </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">
@ -14,5 +14,9 @@
<img style="width: 9em" src="/media/logos/primesound.png" alt="Prime Sound"> <img style="width: 9em" src="/media/logos/primesound.png" alt="Prime Sound">
</a> </a>
</li> </li>
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
<img style="height: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
</a>
</li>
</ul> </ul>
<span class="centered-text-container patreon-link-container"><a href="https://conversejs.org#sponsors">Become a sponsor</a></span> <span class="centered-text-container patreon-link-container"><a href="https://conversejs.org#sponsors">Become a sponsor</a></span>

View File

@ -48,9 +48,9 @@ copyright = u'2018, JC Brand'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '10.1.2' version = '10.1.6'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '10.1.2' 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.

View File

@ -925,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
------------ ------------
@ -2089,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
----------------- -----------------

View File

@ -56,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/10.1.2/dist/converse.min.js * https://cdn.conversejs.org/10.1.6/dist/converse.min.js
* https://cdn.conversejs.org/10.1.2/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/10.1.2/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/10.1.2/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

View File

@ -240,10 +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://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://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://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://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> <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://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
</ul> </ul>
</div> </div>

View File

@ -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/10.1.2/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/10.1.2/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/10.1.2/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/10.1.2/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/10.1.2/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>

View File

@ -104,6 +104,8 @@ module.exports = function(config) {
{ pattern: "src/plugins/omemo/tests/muc.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/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/roomslist/tests/roomslist.js", type: 'module' },

View File

@ -1,7 +0,0 @@
{
"packages": [
".",
"src/*"
],
"version": "4.0.3"
}

View File

@ -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

View 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

View File

@ -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

View File

@ -2,7 +2,7 @@
"short_name": "Converse", "short_name": "Converse",
"name": "Converse Chat", "name": "Converse Chat",
"description": "Messaging Freedom", "description": "Messaging Freedom",
"version": "10.1.2", "version": "10.1.6",
"categories": ["social"], "categories": ["social"],
"icons": [ "icons": [
{ {

View File

@ -19,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/10.1.2/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/10.1.2/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">

22314
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
{ {
"name": "converse.js", "name": "converse.js",
"version": "10.1.2", "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": [ "workspaces": [
"src/headless" "src/headless"
], ],
@ -15,6 +15,7 @@
"dist/", "dist/",
"docs/**/*.md", "docs/**/*.md",
"docs/**/*.rst", "docs/**/*.rst",
"sass/**/*.scss",
"src/**/*.html", "src/**/*.html",
"src/**/*.js", "src/**/*.js",
"src/**/*.json", "src/**/*.json",
@ -23,20 +24,23 @@
"src/**/*.pot", "src/**/*.pot",
"src/**/*.scss", "src/**/*.scss",
"src/**/*.svg", "src/**/*.svg",
"src/**/*.txt" "src/**/*.txt",
"3rdparty/*.js"
], ],
"scripts": { "scripts": {
"build": "webpack --config webpack/webpack.build.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", "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",
"dev": "webpack --config webpack/webpack.build.js --mode=development", "dev": "webpack --config webpack/webpack.build.js --mode=development",
"headless": "webpack --config webpack/webpack.headless.js", "headless": "webpack --config webpack/webpack.headless.js",
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development", "headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
"lerna": "lerna bootstrap --hoist --ignore-scripts",
"nodeps": "webpack --config webpack/webpack.nodeps.js", "nodeps": "webpack --config webpack/webpack.nodeps.js",
"prepare": "npm run lerna && npm run build",
"serve": "webpack serve --config webpack/webpack.serve.js", "serve": "webpack serve --config webpack/webpack.serve.js",
"watch": "webpack --watch --config webpack/webpack.build.js --mode=development" "watch": "webpack --watch --config webpack/webpack.build.js --mode=development",
"types": "tsc --declaration --emitDeclarationOnly --allowJs",
"check:types": "tsc --noEmit"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -65,13 +69,13 @@
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.17.10", "@babel/cli": "^7.17.10",
"@babel/core": "^7.18.5", "@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.18.9",
"@babel/preset-env": "^7.18.2", "@babel/preset-env": "^7.18.2",
"@converse/headless": "file:src/headless", "@converse/headless": "file:src/headless",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"autoprefixer": "^10.4.5", "autoprefixer": "^10.4.5",
"babel-loader": "^9.1.0", "babel-loader": "^9.1.0",
"bootstrap.native-loader": "2.0.0", "bootstrap.native-loader": "2.0.0",
"clean-css-cli": "^5.6.0", "clean-css-cli": "^5.6.2",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"eslint": "^8.21.0", "eslint": "^8.21.0",
@ -87,41 +91,43 @@
"karma-jasmine": "^5.0.0", "karma-jasmine": "^5.0.0",
"karma-jasmine-html-reporter": "^2.0.0", "karma-jasmine-html-reporter": "^2.0.0",
"karma-webpack": "^5.0.0", "karma-webpack": "^5.0.0",
"lerna": "^6.0.1",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
"minimist": "^1.2.6", "minimist": "^1.2.6",
"po-loader": "0.7.0", "po-loader": "0.7.0",
"po2json": "^1.0.0-beta-3", "po2json": "^1.0.0-beta-3",
"postcss": "^8.4.14", "postcss": "^8.4.16",
"postcss-clean": "1.2.0",
"postcss-loader": "^7.0.1", "postcss-loader": "^7.0.1",
"prettierx": "^0.19.0", "prettierx": "^0.19.0",
"sass": "^1.51.0", "sass": "^1.51.0",
"sass-loader": "^13.1.0", "sass-loader": "^13.1.0",
"style-loader": "^3.1.0", "style-loader": "^3.1.0",
"webpack": "^5.72.0", "tsc": "^2.0.4",
"webpack-cli": "^4.7.2", "typescript": "^4.9.5",
"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-dev-server": "^4.8.1",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"@converse/openpromise": "^0.0.1", "@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.8", "@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", "client-compress": "^2.2.2",
"dayjs": "1.11.6", "dayjs": "^1.11.8",
"dompurify": "^2.3.1", "dompurify": "^2.3.1",
"favico.js-slevomat": "^0.3.11", "favico.js-slevomat": "^0.3.11",
"filesize": "^7.0.0", "gifuct-js": "^2.1.2",
"jed": "1.1.1", "jed": "1.1.1",
"lit": "^2.4.0", "lit": "^2.4.0",
"localforage-webextensionstorage-driver": "^3.0.0", "localforage-webextensionstorage-driver": "^3.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.6.0", "strophe.js": "^1.6.2",
"urijs": "^1.19.10" "urijs": "^1.19.10"
}, },
"resolutions": { "resolutions": {

View File

@ -2,6 +2,5 @@
module.exports = { module.exports = {
plugins: [ plugins: [
require('autoprefixer'), require('autoprefixer'),
require('postcss-clean')
] ]
} }

View File

@ -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;
} }

View File

@ -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.

View File

@ -2,684 +2,16 @@
* @copyright The Converse.js contributors * @copyright The Converse.js contributors
* @license Mozilla Public License (MPLv2) * @license Mozilla Public License (MPLv2)
*/ */
import URI from 'urijs'; import './shared/constants.js';
import _converse from './shared/_converse'; import _converse from './shared/_converse';
import advancedFormat from 'dayjs/plugin/advancedFormat'; import advancedFormat from 'dayjs/plugin/advancedFormat';
import connection_api from './shared/connection/api.js'; import api from './shared/api/index.js';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import i18n from './shared/i18n'; import i18n from './shared/i18n';
import invoke from 'lodash-es/invoke';
import isFunction from 'lodash-es/isFunction';
import log from './log.js';
import pluggable from 'pluggable.js/src/pluggable.js';
import sizzle from 'sizzle';
import u, { setUnloadEvent, replacePromise } from './utils/core.js';
import { CHAT_STATES, KEYCODES } from './shared/constants.js';
import { Collection } from "@converse/skeletor/src/collection";
import { Events } from '@converse/skeletor/src/events.js';
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
import { TimeoutError } from './shared/errors';
import { getOpenPromise } from '@converse/openpromise';
import { html } from 'lit';
import { initAppSettings } from './shared/settings/utils.js';
import { settings_api, user_settings_api } from './shared/settings/api.js';
import { sprintf } from 'sprintf-js';
export { converse } from './shared/api/public.js';
export { _converse }; export { _converse };
export { i18n }; export { i18n };
export { api };
import {
attemptNonPreboundSession,
cleanup,
initClientConfig,
initConnection,
initPlugins,
initSessionStorage,
registerGlobalEventHandlers,
setUserJID,
} from './utils/init.js';
dayjs.extend(advancedFormat); dayjs.extend(advancedFormat);
// Add Strophe Namespaces
Strophe.addNamespace('ACTIVITY', 'http://jabber.org/protocol/activity');
Strophe.addNamespace('CARBONS', 'urn:xmpp:carbons:2');
Strophe.addNamespace('CHATSTATES', 'http://jabber.org/protocol/chatstates');
Strophe.addNamespace('CSI', 'urn:xmpp:csi:0');
Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
Strophe.addNamespace('EME', 'urn:xmpp:eme:0');
Strophe.addNamespace('FASTEN', 'urn:xmpp:fasten:0');
Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
Strophe.addNamespace('MENTIONS', 'urn:xmpp:mmn:0');
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
Strophe.addNamespace('MODERATE', 'urn:xmpp:message-moderate:0');
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
Strophe.addNamespace('OCCUPANTID', 'urn:xmpp:occupant-id:0');
Strophe.addNamespace('OMEMO', 'eu.siacs.conversations.axolotl');
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
Strophe.addNamespace('RAI', 'urn:xmpp:rai:0');
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
Strophe.addNamespace('REGISTER', 'jabber:iq:register');
Strophe.addNamespace('RETRACT', 'urn:xmpp:message-retract:0');
Strophe.addNamespace('ROSTERX', 'http://jabber.org/protocol/rosterx');
Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
Strophe.addNamespace('SID', 'urn:xmpp:sid:0');
Strophe.addNamespace('SPOILER', 'urn:xmpp:spoiler:0');
Strophe.addNamespace('STANZAS', 'urn:ietf:params:xml:ns:xmpp-stanzas');
Strophe.addNamespace('STYLING', 'urn:xmpp:styling:0');
Strophe.addNamespace('VCARD', 'vcard-temp');
Strophe.addNamespace('VCARDUPDATE', 'vcard-temp:x:update');
Strophe.addNamespace('XFORM', 'jabber:x:data');
Strophe.addNamespace('XHTML', 'http://www.w3.org/1999/xhtml');
_converse.VERSION_NAME = "v10.1.2";
Object.assign(_converse, Events);
// Make converse pluggable
pluggable.enable(_converse, '_converse', 'pluggable');
/**
* ### The private API
*
* The private API methods are only accessible via the closured {@link _converse}
* object, which is only available to plugins.
*
* These methods are kept private (i.e. not global) because they may return
* sensitive data which should be kept off-limits to other 3rd-party scripts
* that might be running in the page.
*
* @namespace _converse.api
* @memberOf _converse
*/
export const api = _converse.api = {
connection: connection_api,
settings: settings_api,
/**
* Lets you trigger events, which can be listened to via
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
*
* Some events also double as promises and can be waited on via {@link _converse.api.waitUntil}.
*
* @method _converse.api.trigger
* @param {string} name - The event name
* @param {...any} [argument] - Argument to be passed to the event handler
* @param {object} [options]
* @param {boolean} [options.synchronous] - Whether the event is synchronous or not.
* When a synchronous event is fired, a promise will be returned
* by {@link _converse.api.trigger} which resolves once all the
* event handlers' promises have been resolved.
*/
async trigger (name) {
if (!_converse._events) {
return;
}
const args = Array.from(arguments);
const options = args.pop();
if (options && options.synchronous) {
const events = _converse._events[name] || [];
const event_args = args.splice(1);
await Promise.all(events.map(e => e.callback.apply(e.ctx, event_args)));
} else {
_converse.trigger.apply(_converse, arguments);
}
const promise = _converse.promises[name];
if (promise !== undefined) {
promise.resolve();
}
},
/**
* Triggers a hook which can be intercepted by registered listeners via
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}.
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
* A hook is a special kind of event which allows you to intercept a data
* structure in order to modify it, before passing it back.
* @async
* @param {string} name - The hook name
* @param {...any} context - The context to which the hook applies (could be for example, a {@link _converse.ChatBox)).
* @param {...any} data - The data structure to be intercepted and modified by the hook listeners.
* @returns {Promise<any>} - A promise that resolves with the modified data structure.
*/
hook (name, context, data) {
const events = _converse._events[name] || [];
if (events.length) {
// Create a chain of promises, with each one feeding its output to
// the next. The first input is a promise with the original data
// sent to this hook.
return events.reduce((o, e) => o.then(d => e.callback(context, d)), Promise.resolve(data));
} else {
return data;
}
},
/**
* This grouping collects API functions related to the current logged in user.
*
* @namespace _converse.api.user
* @memberOf _converse.api
*/
user: {
settings: user_settings_api,
/**
* @method _converse.api.user.jid
* @returns {string} The current user's full JID (Jabber ID)
* @example _converse.api.user.jid())
*/
jid () {
return _converse.connection.jid;
},
/**
* Logs the user in.
*
* If called without any parameters, Converse will try
* to log the user in by calling the `prebind_url` or `credentials_url` depending
* on whether prebinding is used or not.
*
* @method _converse.api.user.login
* @param {string} [jid]
* @param {string} [password]
* @param {boolean} [automatic=false] - An internally used flag that indicates whether
* this method was called automatically once the connection has been
* initialized. It's used together with the `auto_login` configuration flag
* to determine whether Converse should try to log the user in if it
* fails to restore a previous auth'd session.
* @returns {void}
*/
async login (jid, password, automatic=false) {
jid = jid || api.settings.get('jid');
if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
initConnection();
}
if (api.settings.get("connection_options")?.worker && (await _converse.connection.restoreWorkerSession())) {
return;
}
if (jid) {
jid = await setUserJID(jid);
}
// See whether there is a BOSH session to re-attach to
const bosh_plugin = _converse.pluggable.plugins['converse-bosh'];
if (bosh_plugin?.enabled()) {
if (await _converse.restoreBOSHSession()) {
return;
} else if (api.settings.get("authentication") === _converse.PREBIND && (!automatic || api.settings.get("auto_login"))) {
return _converse.startNewPreboundBOSHSession();
}
}
password = password || api.settings.get("password");
const credentials = (jid && password) ? { jid, password } : null;
attemptNonPreboundSession(credentials, automatic);
},
/**
* Logs the user out of the current XMPP session.
* @method _converse.api.user.logout
* @example _converse.api.user.logout();
*/
async logout () {
/**
* Triggered before the user is logged out
* @event _converse#beforeLogout
*/
await api.trigger('beforeLogout', {'synchronous': true});
const promise = getOpenPromise();
const complete = () => {
// Recreate all the promises
Object.keys(_converse.promises).forEach(replacePromise);
delete _converse.jid
// Remove the session JID, otherwise the user would just be logged
// in again upon reload. See #2759
localStorage.removeItem('conversejs-session-jid');
/**
* Triggered once the user has logged out.
* @event _converse#logout
*/
api.trigger('logout');
promise.resolve();
}
_converse.connection.setDisconnectionCause(_converse.LOGOUT, undefined, true);
if (_converse.connection !== undefined) {
api.listen.once('disconnected', () => complete());
_converse.connection.disconnect();
} else {
complete();
}
return promise;
}
},
/**
* Converse and its plugins trigger various events which you can listen to via the
* {@link _converse.api.listen} namespace.
*
* Some of these events are also available as [ES2015 Promises](http://es6-features.org/#PromiseUsage)
* although not all of them could logically act as promises, since some events
* might be fired multpile times whereas promises are to be resolved (or
* rejected) only once.
*
* Events which are also promises include:
*
* * [cachedRoster](/docs/html/events.html#cachedroster)
* * [chatBoxesFetched](/docs/html/events.html#chatBoxesFetched)
* * [pluginsInitialized](/docs/html/events.html#pluginsInitialized)
* * [roster](/docs/html/events.html#roster)
* * [rosterContactsFetched](/docs/html/events.html#rosterContactsFetched)
* * [rosterGroupsFetched](/docs/html/events.html#rosterGroupsFetched)
* * [rosterInitialized](/docs/html/events.html#rosterInitialized)
*
* The various plugins might also provide promises, and they do this by using the
* `promises.add` api method.
*
* @namespace _converse.api.promises
* @memberOf _converse.api
*/
promises: {
/**
* By calling `promises.add`, a new [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* is made available for other code or plugins to depend on via the
* {@link _converse.api.waitUntil} method.
*
* Generally, it's the responsibility of the plugin which adds the promise to
* also resolve it.
*
* This is done by calling {@link _converse.api.trigger}, which not only resolves the
* promise, but also emits an event with the same name (which can be listened to
* via {@link _converse.api.listen}).
*
* @method _converse.api.promises.add
* @param {string|array} [name|names] The name or an array of names for the promise(s) to be added
* @param {boolean} [replace=true] Whether this promise should be replaced with a new one when the user logs out.
* @example _converse.api.promises.add('foo-completed');
*/
add (promises, replace=true) {
promises = Array.isArray(promises) ? promises : [promises];
promises.forEach(name => {
const promise = getOpenPromise();
promise.replace = replace;
_converse.promises[name] = promise;
});
}
},
/**
* Converse emits events to which you can subscribe to.
*
* The `listen` namespace exposes methods for creating event listeners
* (aka handlers) for these events.
*
* @namespace _converse.api.listen
* @memberOf _converse
*/
listen: {
/**
* Lets you listen to an event exactly once.
* @method _converse.api.listen.once
* @param {string} name The event's name
* @param {function} callback The callback method to be called when the event is emitted.
* @param {object} [context] The value of the `this` parameter for the callback.
* @example _converse.api.listen.once('message', function (messageXML) { ... });
*/
once: _converse.once.bind(_converse),
/**
* Lets you subscribe to an event.
* Every time the event fires, the callback method specified by `callback` will be called.
* @method _converse.api.listen.on
* @param {string} name The event's name
* @param {function} callback The callback method to be called when the event is emitted.
* @param {object} [context] The value of the `this` parameter for the callback.
* @example _converse.api.listen.on('message', function (messageXML) { ... });
*/
on: _converse.on.bind(_converse),
/**
* To stop listening to an event, you can use the `not` method.
* @method _converse.api.listen.not
* @param {string} name The event's name
* @param {function} callback The callback method that is to no longer be called when the event fires
* @example _converse.api.listen.not('message', function (messageXML);
*/
not: _converse.off.bind(_converse),
/**
* Subscribe to an incoming stanza
* Every a matched stanza is received, the callback method specified by
* `callback` will be called.
* @method _converse.api.listen.stanza
* @param {string} name The stanza's name
* @param {object} options Matching options (e.g. 'ns' for namespace, 'type' for stanza type, also 'id' and 'from');
* @param {function} handler The callback method to be called when the stanza appears
*/
stanza (name, options, handler) {
if (isFunction(options)) {
handler = options;
options = {};
} else {
options = options || {};
}
_converse.connection.addHandler(
handler,
options.ns,
name,
options.type,
options.id,
options.from,
options
);
}
},
/**
* Wait until a promise is resolved or until the passed in function returns
* a truthy value.
* @method _converse.api.waitUntil
* @param {string|function} condition - The name of the promise to wait for,
* or a function which should eventually return a truthy value.
* @returns {Promise}
*/
waitUntil (condition) {
if (isFunction(condition)) {
return u.waitUntil(condition);
} else {
const promise = _converse.promises[condition];
if (promise === undefined) {
return null;
}
return promise;
}
},
/**
* Allows you to send XML stanzas.
* @method _converse.api.send
* @param {XMLElement} stanza
* @return {void}
* @example
* const msg = converse.env.$msg({
* 'from': 'juliet@example.com/balcony',
* 'to': 'romeo@example.net',
* 'type':'chat'
* });
* _converse.api.send(msg);
*/
send (stanza) {
if (!api.connection.connected()) {
log.warn("Not sending stanza because we're not connected!");
log.warn(Strophe.serialize(stanza));
return;
}
if (typeof stanza === 'string') {
stanza = u.toStanza(stanza);
} else if (stanza?.tree) {
stanza = stanza.tree();
}
if (stanza.tagName === 'iq') {
return api.sendIQ(stanza);
} else {
_converse.connection.send(stanza);
api.trigger('send', stanza);
}
},
/**
* Send an IQ stanza
* @method _converse.api.sendIQ
* @param {XMLElement} stanza
* @param {Integer} [timeout=_converse.STANZA_TIMEOUT]
* @param {Boolean} [reject=true] - Whether an error IQ should cause the promise
* to be rejected. If `false`, the promise will resolve instead of being rejected.
* @returns {Promise} A promise which resolves (or potentially rejected) once we
* receive a `result` or `error` stanza or once a timeout is reached.
* If the IQ stanza being sent is of type `result` or `error`, there's
* nothing to wait for, so an already resolved promise is returned.
*/
sendIQ (stanza, timeout=_converse.STANZA_TIMEOUT, reject=true) {
const { connection } = _converse;
let promise;
stanza = stanza.tree?.() ?? stanza;
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
timeout = timeout || _converse.STANZA_TIMEOUT;
if (reject) {
promise = new Promise((resolve, reject) => connection.sendIQ(stanza, resolve, reject, timeout));
promise.catch((e) => {
if (e === null) {
throw new TimeoutError(
`Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
);
}
});
} else {
promise = new Promise((resolve) => connection.sendIQ(stanza, resolve, resolve, timeout));
}
} else {
_converse.connection.sendIQ(stanza);
promise = Promise.resolve();
}
api.trigger('send', stanza);
return promise;
}
};
_converse.shouldClearCache = () => (
!_converse.config.get('trusted') ||
api.settings.get('clear_cache_on_logout') ||
_converse.isTestEnv()
);
_converse.ConnectionFeedback = Model.extend({
defaults: {
'connection_status': Strophe.Status.DISCONNECTED,
'message': ''
},
initialize () {
this.on('change', () => api.trigger('connfeedback', _converse.connfeedback));
}
});
export const converse = window.converse || {};
/**
* ### The Public API
*
* This namespace contains public API methods which are are
* accessible on the global `converse` object.
* They are public, because any JavaScript in the
* page can call them. Public methods therefore dont expose any sensitive
* or closured data. To do that, youll need to create a plugin, which has
* access to the private API method.
*
* @global
* @namespace converse
*/
Object.assign(converse, {
CHAT_STATES,
keycodes: KEYCODES,
/**
* Public API method which initializes Converse.
* This method must always be called when using Converse.
* @async
* @memberOf converse
* @method initialize
* @param {object} config A map of [configuration-settings](https://conversejs.org/docs/html/configuration.html#configuration-settings).
* @example
* converse.initialize({
* auto_list_rooms: false,
* auto_subscribe: false,
* bosh_service_url: 'https://bind.example.com',
* hide_muc_server: false,
* i18n: 'en',
* play_sounds: true,
* show_controlbox_by_default: true,
* debug: false,
* roster_groups: true
* });
*/
async initialize (settings) {
await cleanup(_converse);
setUnloadEvent();
initAppSettings(settings);
_converse.strict_plugin_dependencies = settings.strict_plugin_dependencies; // Needed by pluggable.js
log.setLogLevel(api.settings.get("loglevel"));
if (api.settings.get("authentication") === _converse.ANONYMOUS) {
if (api.settings.get("auto_login") && !api.settings.get('jid')) {
throw new Error("Config Error: you need to provide the server's " +
"domain via the 'jid' option when using anonymous " +
"authentication with auto_login.");
}
}
_converse.router.route(
/^converse\?loglevel=(debug|info|warn|error|fatal)$/, 'loglevel',
l => log.setLogLevel(l)
);
_converse.connfeedback = new _converse.ConnectionFeedback();
/* When reloading the page:
* For new sessions, we need to send out a presence stanza to notify
* the server/network that we're online.
* When re-attaching to an existing session we don't need to again send out a presence stanza,
* because it's as if "we never left" (see onConnectStatusChanged).
* https://github.com/conversejs/converse.js/issues/521
*/
_converse.send_initial_presence = true;
await initSessionStorage(_converse);
await initClientConfig(_converse);
await i18n.initialize();
initPlugins(_converse);
// Register all custom elements
// XXX: api.elements is defined in the UI part of Converse, outside of @converse/headless.
// This line should probably be moved to the UI code as part of a larger refactoring.
api.elements?.register();
registerGlobalEventHandlers(_converse);
try {
!History.started && _converse.router.history.start();
} catch (e) {
log.error(e);
}
const plugins = _converse.pluggable.plugins
if (api.settings.get("auto_login") || api.settings.get("keepalive") && invoke(plugins['converse-bosh'], 'enabled')) {
await api.user.login(null, null, true);
}
/**
* Triggered once converse.initialize has finished.
* @event _converse#initialized
*/
api.trigger('initialized');
if (_converse.isTestEnv()) {
return _converse;
}
},
/**
* Exposes methods for adding and removing plugins. You'll need to write a plugin
* if you want to have access to the private API methods defined further down below.
*
* For more information on plugins, read the documentation on [writing a plugin](/docs/html/plugin_development.html).
* @namespace plugins
* @memberOf converse
*/
plugins: {
/**
* Registers a new plugin.
* @method converse.plugins.add
* @param {string} name The name of the plugin
* @param {object} plugin The plugin object
* @example
* const plugin = {
* initialize: function () {
* // Gets called as soon as the plugin has been loaded.
*
* // Inside this method, you have access to the private
* // API via `_covnerse.api`.
*
* // The private _converse object contains the core logic
* // and data-structures of Converse.
* }
* }
* converse.plugins.add('myplugin', plugin);
*/
add (name, plugin) {
plugin.__name__ = name;
if (_converse.pluggable.plugins[name] !== undefined) {
throw new TypeError(
`Error: plugin with name "${name}" has already been ` + 'registered!'
);
} else {
_converse.pluggable.plugins[name] = plugin;
}
}
},
/**
* Utility methods and globals from bundled 3rd party libraries.
* @typedef ConverseEnv
* @property {function} converse.env.$build - Creates a Strophe.Builder, for creating stanza objects.
* @property {function} converse.env.$iq - Creates a Strophe.Builder with an <iq/> element as the root.
* @property {function} converse.env.$msg - Creates a Strophe.Builder with an <message/> element as the root.
* @property {function} converse.env.$pres - Creates a Strophe.Builder with an <presence/> element as the root.
* @property {function} converse.env.Promise - The Promise implementation used by Converse.
* @property {function} converse.env.Strophe - The [Strophe](http://strophe.im/strophejs) XMPP library used by Converse.
* @property {function} converse.env.f - And instance of Lodash with its methods wrapped to produce immutable auto-curried iteratee-first data-last methods.
* @property {function} converse.env.sizzle - [Sizzle](https://sizzlejs.com) CSS selector engine.
* @property {function} converse.env.sprintf
* @property {object} converse.env._ - The instance of [lodash-es](http://lodash.com) used by Converse.
* @property {object} converse.env.dayjs - [DayJS](https://github.com/iamkun/dayjs) date manipulation library.
* @property {object} converse.env.utils - Module containing common utility methods used by Converse.
* @memberOf converse
*/
'env': {
$build,
$iq,
$msg,
$pres,
'utils': u,
Collection,
Model,
Promise,
Strophe,
URI,
dayjs,
html,
log,
sizzle,
sprintf,
stx: u.stx,
u,
}
});

View File

@ -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)) {
@ -42,7 +44,7 @@ const log = {
* logged as well. * logged as well.
* @method log#log * @method log#log
* @param { string | Error } 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;

View File

@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"@converse/openpromise": "^0.0.1", "@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.8", "@converse/skeletor": "0.0.8",
"dayjs": "1.11.6", "dayjs": "1.11.8",
"dompurify": "^2.3.1", "dompurify": "^2.3.1",
"filesize": "^7.0.0", "filesize": "^7.0.0",
"localforage-webextensionstorage-driver": "^3.0.0", "localforage-webextensionstorage-driver": "^3.0.0",

View File

@ -1,16 +1,15 @@
{ {
"name": "@converse/headless", "name": "@converse/headless",
"version": "10.1.2", "version": "10.1.6",
"description": "Converse.js Headless build", "description": "Converse.js Headless build",
"author": { "author": "JC Brand <jc@opkode.com>",
"name": "JC Brand", "contributors": [
"email": "jc@opkode.com" "cmrd Senya <senya@riseup.net>"
}, ],
"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.js", "main": "dist/converse-headless.js",
"module": "headless.js", "module": "index.js",
"keywords": [ "keywords": [
"converse.js", "converse.js",
"XMPP", "XMPP",
@ -31,19 +30,19 @@
"bugs": { "bugs": {
"url": "https://github.com/conversejs/converse.js/issues" "url": "https://github.com/conversejs/converse.js/issues"
}, },
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
"dependencies": { "dependencies": {
"@converse/openpromise": "^0.0.1", "@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.8", "@converse/skeletor": "^0.0.8",
"dayjs": "1.11.6", "dayjs": "^1.11.8",
"dompurify": "^2.3.1", "dompurify": "^2.3.1",
"filesize": "^7.0.0", "filesize": "^10.0.7",
"localforage-webextensionstorage-driver": "^3.0.0", "localforage-webextensionstorage-driver": "^3.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.6.0", "strophe.js": "^1.6.2",
"urijs": "^1.19.10" "urijs": "^1.19.10"
} },
"devDependencies": {}
} }

View File

@ -136,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');
*/ */

View File

@ -37,7 +37,7 @@ async function createCapsNode () {
/** /**
* Given a stanza, adds a XEP-0115 CAPS element * Given a stanza, adds a XEP-0115 CAPS element
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
export async function addCapsNode (stanza) { export async function addCapsNode (stanza) {
const caps_el = await createCapsNode(); const caps_el = await createCapsNode();

View File

@ -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

View File

@ -1,12 +1,13 @@
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 { debouncedPruneHistory, handleCorrection } from '@converse/headless/shared/chat/utils.js';
import { filesize } from "filesize";
import { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js'; 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';
@ -368,7 +369,7 @@ 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,

View File

@ -32,7 +32,7 @@ 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) }
*/ */

View File

@ -1,7 +1,8 @@
import log from '@converse/headless/log.js';
import { _converse, api, converse } from '@converse/headless/core.js'; import { _converse, api, converse } from '@converse/headless/core.js';
import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers'; import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
import { parseMessage } from './parsers.js'; import { parseMessage } from './parsers.js';
import log from '@converse/headless/log.js'; import { shouldClearCache } from '@converse/headless/utils/core.js';
const { Strophe, u } = converse.env; const { Strophe, u } = converse.env;
@ -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 }))
); );
@ -113,7 +114,7 @@ export async function handleMessageStanza (stanza) {
* @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
*/ */

View File

@ -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(() => { ... });
*/ */

View File

@ -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) {
@ -209,7 +209,7 @@ export default {
* Return any disco items advertised on this entity * Return any disco items advertised on this entity
* *
* @method api.disco.entities.items * @method api.disco.entities.items
* @param {string} jid The Jabber ID of the entity for which we want to fetch items * @param { string } jid The Jabber ID of the entity for which we want to fetch items
* @example api.disco.entities.items(jid); * @example api.disco.entities.items(jid);
*/ */
items (jid) { items (jid) {
@ -225,12 +225,12 @@ 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 {object} data * @param { object } data
* @param {string} data.jid - The Jabber ID of the entity * @param { string } data.jid - The Jabber ID of the entity
* @param {string} data.parent_jid - The Jabber ID of the parent entity * @param { string } data.parent_jid - The Jabber ID of the parent entity
* @param {string} data.name * @param { string } data.name
* @param {object} [options] - Additional options * @param { object } [options] - Additional options
* @param {boolean} [options.ignore_cache] * @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});
*/ */
@ -248,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
@ -285,11 +285,11 @@ export default {
* associated items, supports a given feature. * associated items, supports a given feature.
* *
* @method api.disco.features.has * @method api.disco.features.has
* @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 boolean * @returns {Promise} A promise which resolves with a boolean
* @example * @example
@ -319,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
@ -341,7 +341,7 @@ export default {
* 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');
@ -379,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');
@ -401,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');
*/ */
@ -424,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

View File

@ -3,6 +3,7 @@
* @copyright 2022, the Converse.js contributors * @copyright 2022, the Converse.js contributors
* @license Mozilla Public License (MPLv2) * @license Mozilla Public License (MPLv2)
*/ */
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';
@ -85,8 +86,8 @@ 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));

View File

@ -163,7 +163,7 @@ function shortnamesToUnicode (str) {
* Determines whether the passed in string is just a single emoji shortname; * Determines whether the passed in string is just a single emoji shortname;
* @namespace u * @namespace u
* @method u.isOnlyEmojis * @method u.isOnlyEmojis
* @param { String } shortname - A string which migh be just an emoji shortname * @param { String } text - A string which migh be just an emoji shortname
* @returns { Boolean } * @returns { Boolean }
*/ */
function isOnlyEmojis (text) { function isOnlyEmojis (text) {

View File

@ -15,8 +15,8 @@ export default {
* *
* @method api.headlines.get * @method api.headlines.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.HeadlinesFeed> } * @returns { Promise<_converse.HeadlinesFeed> }
*/ */
async get (jids, attrs={}, create=false) { async get (jids, attrs={}, create=false) {

View File

@ -4,7 +4,7 @@ import { parseMessage } from '@converse/headless/plugins/chat/parsers';
/** /**
* Handler method for all incoming messages of type "headline". * Handler method for all incoming messages of type "headline".
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
export async function onHeadlineMessage (stanza) { export async function onHeadlineMessage (stanza) {
if (isHeadline(stanza) || isServerMessage(stanza)) { if (isHeadline(stanza) || isServerMessage(stanza)) {

View File

@ -1,6 +1,7 @@
import { RSM } from '@converse/headless/shared/rsm';
import log from '@converse/headless/log'; import log from '@converse/headless/log';
import sizzle from "sizzle"; import sizzle from "sizzle";
import { RSM } from '@converse/headless/shared/rsm';
import { TimeoutError } from '../../shared/errors.js';
import { _converse, api, converse } from "@converse/headless/core"; import { _converse, api, converse } from "@converse/headless/core";
const { Strophe, $iq, dayjs } = converse.env; const { Strophe, $iq, dayjs } = converse.env;
@ -270,7 +271,7 @@ export default {
const { __ } = _converse; const { __ } = _converse;
const err_msg = __("Timeout while trying to fetch archived messages."); const err_msg = __("Timeout while trying to fetch archived messages.");
log.error(err_msg); log.error(err_msg);
error = new _converse.TimeoutError(err_msg); error = new TimeoutError(err_msg);
return { messages, error }; return { messages, error };
} else if (u.isErrorStanza(iq_result)) { } else if (u.isErrorStanza(iq_result)) {

View File

@ -100,7 +100,7 @@ export async function handleMAMResult (model, result, query, options, should_pag
/** /**
* @typedef { Object } MAMOptions * @typedef { Object } MAMOptions
* A map of MAM related options that may be passed to fetchArchivedMessages * A map of MAM related options that may be passed to fetchArchivedMessages
* @param { integer } [options.max] - The maximum number of items to return. * @param { number } [options.max] - The maximum number of items to return.
* Defaults to "archived_messages_page_size" * Defaults to "archived_messages_page_size"
* @param { string } [options.after] - The XEP-0359 stanza ID of a message * @param { string } [options.after] - The XEP-0359 stanza ID of a message
* after which messages should be returned. Implies forward paging. * after which messages should be returned. Implies forward paging.
@ -117,7 +117,7 @@ export async function handleMAMResult (model, result, query, options, should_pag
/** /**
* Fetch XEP-0313 archived messages based on the passed in criteria. * Fetch XEP-0313 archived messages based on the passed in criteria.
* @param { _converse.ChatBox | _converse.ChatRoom } model * @param { ChatBox | ChatRoom } model
* @param { MAMOptions } [options] * @param { MAMOptions } [options]
* @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether * @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether
* this function should recursively page through the entire result set if a limited * this function should recursively page through the entire result set if a limited

View File

@ -45,7 +45,7 @@ export async function getAffiliationList (affiliation, muc_jid) {
} }
/** /**
* Given an occupant model, see which affiliations may be assigned to that user. * Given an occupant model, see which affiliations may be assigned by that user
* @param { Model } occupant * @param { Model } occupant
* @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations * @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations
*/ */
@ -54,9 +54,9 @@ export function getAssignableAffiliations (occupant) {
if (!Array.isArray(disabled)) { if (!Array.isArray(disabled)) {
disabled = disabled ? AFFILIATIONS : []; disabled = disabled ? AFFILIATIONS : [];
} }
if (occupant.get('affiliation') === 'owner') { if (occupant?.get('affiliation') === 'owner') {
return AFFILIATIONS.filter(a => !disabled.includes(a)); return AFFILIATIONS.filter(a => !disabled.includes(a));
} else if (occupant.get('affiliation') === 'admin') { } else if (occupant?.get('affiliation') === 'admin') {
return AFFILIATIONS.filter(a => !['owner', 'admin', ...disabled].includes(a)); return AFFILIATIONS.filter(a => !['owner', 'admin', ...disabled].includes(a));
} else { } else {
return []; return [];

View File

@ -23,7 +23,7 @@ export default {
* @method api.rooms.create * @method api.rooms.create
* @param {(string[]|string)} jid|jids The JID or array of * @param {(string[]|string)} jid|jids The JID or array of
* JIDs of the chatroom(s) to create * JIDs of the chatroom(s) to create
* @param {object} [attrs] attrs The room attributes * @param { object } [attrs] attrs The room attributes
* @returns {Promise} Promise which resolves with the Model representing the chat. * @returns {Promise} Promise which resolves with the Model representing the chat.
*/ */
create (jids, attrs = {}) { create (jids, attrs = {}) {
@ -45,24 +45,24 @@ export default {
* Similar to {@link api.chats.open}, but for groupchats. * Similar to {@link api.chats.open}, but for groupchats.
* *
* @method api.rooms.open * @method api.rooms.open
* @param {string} jid The room JID or JIDs (if not specified, all * @param { string } jid The room JID or JIDs (if not specified, all
* currently open rooms will be returned). * currently open rooms will be returned).
* @param {string} attrs A map containing any extra room attributes. * @param { string } attrs A map containing any extra room attributes.
* @param {string} [attrs.nick] The current user's nickname for the MUC * @param { string } [attrs.nick] The current user's nickname for the MUC
* @param {boolean} [attrs.auto_configure] A boolean, indicating * @param { boolean } [attrs.auto_configure] A boolean, indicating
* whether the room should be configured automatically or not. * whether the room should be configured automatically or not.
* If set to `true`, then it makes sense to pass in configuration settings. * If set to `true`, then it makes sense to pass in configuration settings.
* @param {object} [attrs.roomconfig] A map of configuration settings to be used when the room gets * @param { object } [attrs.roomconfig] A map of configuration settings to be used when the room gets
* configured automatically. Currently it doesn't make sense to specify * configured automatically. Currently it doesn't make sense to specify
* `roomconfig` values if `auto_configure` is set to `false`. * `roomconfig` values if `auto_configure` is set to `false`.
* For a list of configuration values that can be passed in, refer to these values * For a list of configuration values that can be passed in, refer to these values
* in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner). * in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
* The values should be named without the `muc#roomconfig_` prefix. * The values should be named without the `muc#roomconfig_` prefix.
* @param {boolean} [attrs.minimized] A boolean, indicating whether the room should be opened minimized or not. * @param { boolean } [attrs.minimized] A boolean, indicating whether the room should be opened minimized or not.
* @param {boolean} [attrs.bring_to_foreground] A boolean indicating whether the room should be * @param { boolean } [attrs.bring_to_foreground] A boolean indicating whether the room should be
* brought to the foreground and therefore replace the currently shown chat. * brought to the foreground and therefore replace the currently shown chat.
* If there is no chat currently open, then this option is ineffective. * If there is no chat currently open, then this option is ineffective.
* @param {Boolean} [force=false] - By default, a minimized * @param { Boolean } [force=false] - By default, a minimized
* room won't be maximized (in `overlayed` view mode) and in * room won't be maximized (in `overlayed` view mode) and in
* `fullscreen` view mode a newly opened room won't replace * `fullscreen` view mode a newly opened room won't replace
* another chat already in the foreground. * another chat already in the foreground.

View File

@ -1,4 +1,3 @@
import log from '../../log';
import { Strophe } from 'strophe.js/src/strophe'; import { Strophe } from 'strophe.js/src/strophe';
import { _converse, api } from '../../core.js'; import { _converse, api } from '../../core.js';
@ -20,6 +19,7 @@ const ChatRoomMessageMixin = {
this.on('change:type', () => this.setOccupant()); this.on('change:type', () => this.setOccupant());
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage()); this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
this.chatbox = this.collection?.chatbox;
this.setTimerForEphemeralMessage(); this.setTimerForEphemeralMessage();
this.setOccupant(); this.setOccupant();
/** /**
@ -53,24 +53,20 @@ const ChatRoomMessageMixin = {
return ( return (
['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) && ['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) &&
this.get(`stanza_id ${this.get('from_muc')}`) && this.get(`stanza_id ${this.get('from_muc')}`) &&
this.collection.chatbox.canModerateMessages() this.chatbox.canModerateMessages()
); );
}, },
checkValidity () { checkValidity () {
const result = _converse.Message.prototype.checkValidity.call(this); const result = _converse.Message.prototype.checkValidity.call(this);
!result && this.collection.chatbox.debouncedRejoin(); !result && this.chatbox.debouncedRejoin();
return result; return result;
}, },
onOccupantRemoved () { onOccupantRemoved () {
this.stopListening(this.occupant); this.stopListening(this.occupant);
delete this.occupant; delete this.occupant;
const chatbox = this?.collection?.chatbox; this.listenTo(this.chatbox.occupants, 'add', this.onOccupantAdded);
if (!chatbox) {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
}, },
onOccupantAdded (occupant) { onOccupantAdded (occupant) {
@ -81,10 +77,6 @@ const ChatRoomMessageMixin = {
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) { } else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
return; return;
} }
const chatbox = this?.collection?.chatbox;
if (!chatbox) {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
this.occupant = occupant; this.occupant = occupant;
if (occupant.get('jid')) { if (occupant.get('jid')) {
@ -93,32 +85,40 @@ const ChatRoomMessageMixin = {
this.trigger('occupantAdded'); this.trigger('occupantAdded');
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved); this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
this.stopListening(chatbox.occupants, 'add', this.onOccupantAdded); this.stopListening(this.chatbox.occupants, 'add', this.onOccupantAdded);
},
getOccupant() {
if (this.occupant) return this.occupant;
this.setOccupant();
return this.occupant;
}, },
setOccupant () { setOccupant () {
if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) { if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
return; return;
} }
const chatbox = this?.collection?.chatbox;
if (!chatbox) {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
const nick = Strophe.getResourceFromJid(this.get('from')); const nick = Strophe.getResourceFromJid(this.get('from'));
const occupant_id = this.get('occupant_id'); const occupant_id = this.get('occupant_id');
this.occupant = chatbox.occupants.findOccupant({ nick, occupant_id });
if (!this.occupant && api.settings.get('muc_send_probes')) { this.occupant = this.chatbox.occupants.findOccupant({ nick, occupant_id });
this.occupant = chatbox.occupants.create({ nick, occupant_id, 'type': 'unavailable' });
const jid = `${chatbox.get('jid')}/${nick}`; if (!this.occupant) {
this.occupant = this.chatbox.occupants.create({
nick,
occupant_id,
jid: this.get('from_real_jid'),
});
if (api.settings.get('muc_send_probes')) {
const jid = `${this.chatbox.get('jid')}/${nick}`;
api.user.presence.send('probe', jid); api.user.presence.send('probe', jid);
} }
if (this.occupant) {
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
} else {
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
} }
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
} }
}; };

View File

@ -1,22 +1,22 @@
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import invoke from 'lodash-es/invoke';
import isElement from 'lodash-es/isElement'; import isElement from 'lodash-es/isElement';
import log from '../../log'; import log from '../../log';
import p from '../../utils/parse-helpers'; import p from '../../utils/parse-helpers';
import pick from 'lodash-es/pick'; import pick from 'lodash-es/pick';
import sizzle from 'sizzle'; import sizzle from 'sizzle';
import { Model } from '@converse/skeletor/src/model.js'; import { Model } from '@converse/skeletor/src/model.js';
import { ROOMSTATUS } from './constants.js';
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe'; import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
import { TimeoutError } from '../../shared/errors.js';
import { _converse, api, converse } from '../../core.js'; import { _converse, api, converse } from '../../core.js';
import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js'; import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js';
import { handleCorrection } from '../../shared/chat/utils.js';
import { getOpenPromise } from '@converse/openpromise'; import { getOpenPromise } from '@converse/openpromise';
import { handleCorrection } from '../../shared/chat/utils.js';
import { initStorage } from '../../utils/storage.js'; import { initStorage } from '../../utils/storage.js';
import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js'; import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
import { isUniView, getUniqueId, safeSave } from '../../utils/core.js'; import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
import { parseMUCMessage, parseMUCPresence } from './parsers.js'; import { parseMUCMessage, parseMUCPresence } from './parsers.js';
import { sendMarker } from '../../shared/actions.js'; import { sendMarker } from '../../shared/actions.js';
import { ROOMSTATUS } from './constants.js';
const { u } = converse.env; const { u } = converse.env;
@ -226,7 +226,7 @@ const ChatRoomMixin = {
* *Hook* which allows plugins to update an outgoing MUC join presence stanza * *Hook* which allows plugins to update an outgoing MUC join presence stanza
* @event _converse#constructedMUCPresence * @event _converse#constructedMUCPresence
* @param { _converse.ChatRoom } - The MUC from which this message stanza is being sent. * @param { _converse.ChatRoom } - The MUC from which this message stanza is being sent.
* @param { XMLElement } stanza - The stanza which will be sent out * @param { Element } stanza - The stanza which will be sent out
*/ */
stanza = await api.hook('constructedMUCPresence', this, stanza); stanza = await api.hook('constructedMUCPresence', this, stanza);
return stanza; return stanza;
@ -507,7 +507,7 @@ const ChatRoomMixin = {
* Handles incoming message stanzas from the service that hosts this MUC * Handles incoming message stanzas from the service that hosts this MUC
* @private * @private
* @method _converse.ChatRoom#handleMessageFromMUCHost * @method _converse.ChatRoom#handleMessageFromMUCHost
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
handleMessageFromMUCHost (stanza) { handleMessageFromMUCHost (stanza) {
if (this.isEntered()) { if (this.isEntered()) {
@ -528,7 +528,7 @@ const ChatRoomMixin = {
* Handles XEP-0452 MUC Mention Notification messages * Handles XEP-0452 MUC Mention Notification messages
* @private * @private
* @method _converse.ChatRoom#handleForwardedMentions * @method _converse.ChatRoom#handleForwardedMentions
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
handleForwardedMentions (stanza) { handleForwardedMentions (stanza) {
if (this.isEntered()) { if (this.isEntered()) {
@ -558,7 +558,7 @@ const ChatRoomMixin = {
* Parses an incoming message stanza and queues it for processing. * Parses an incoming message stanza and queues it for processing.
* @private * @private
* @method _converse.ChatRoom#handleMessageStanza * @method _converse.ChatRoom#handleMessageStanza
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
async handleMessageStanza (stanza) { async handleMessageStanza (stanza) {
stanza = stanza.tree?.() ?? stanza; stanza = stanza.tree?.() ?? stanza;
@ -709,9 +709,9 @@ const ChatRoomMixin = {
* or error message within a specific timeout period. * or error message within a specific timeout period.
* @private * @private
* @method _converse.ChatRoom#sendTimedMessage * @method _converse.ChatRoom#sendTimedMessage
* @param { _converse.Message|XMLElement } message * @param { _converse.Message|Element } message
* @returns { Promise<XMLElement>|Promise<_converse.TimeoutError> } Returns a promise * @returns { Promise<Element>|Promise<TimeoutError> } Returns a promise
* which resolves with the reflected message stanza or with an error stanza or {@link _converse.TimeoutError}. * which resolves with the reflected message stanza or with an error stanza or {@link TimeoutError}.
*/ */
sendTimedMessage (el) { sendTimedMessage (el) {
if (typeof el.tree === 'function') { if (typeof el.tree === 'function') {
@ -724,9 +724,10 @@ const ChatRoomMixin = {
el.setAttribute('id', id); el.setAttribute('id', id);
} }
const promise = getOpenPromise(); const promise = getOpenPromise();
const timeoutHandler = _converse.connection.addTimedHandler(_converse.STANZA_TIMEOUT, () => { const timeout = api.settings.get('stanza_timeout');
const timeoutHandler = _converse.connection.addTimedHandler(timeout, () => {
_converse.connection.deleteHandler(handler); _converse.connection.deleteHandler(handler);
const err = new _converse.TimeoutError('Timeout Error: No response from server'); const err = new TimeoutError('Timeout Error: No response from server');
promise.resolve(err); promise.resolve(err);
return false; return false;
}); });
@ -776,7 +777,7 @@ const ChatRoomMixin = {
if (u.isErrorStanza(result)) { if (u.isErrorStanza(result)) {
log.error(result); log.error(result);
} else if (result instanceof _converse.TimeoutError) { } else if (result instanceof TimeoutError) {
log.error(result); log.error(result);
message.save({ message.save({
editable, editable,
@ -793,7 +794,7 @@ const ChatRoomMixin = {
* Retract someone else's message in this groupchat. * Retract someone else's message in this groupchat.
* @private * @private
* @method _converse.ChatRoom#retractOtherMessage * @method _converse.ChatRoom#retractOtherMessage
* @param { _converse.Message } message - The message which we're retracting. * @param { _converse.ChatRoomMessage } message - The message which we're retracting.
* @param { string } [reason] - The reason for retracting the message. * @param { string } [reason] - The reason for retracting the message.
* @example * @example
* const room = await api.rooms.get(jid); * const room = await api.rooms.get(jid);
@ -828,7 +829,7 @@ const ChatRoomMixin = {
* Sends an IQ stanza to the XMPP server to retract a message in this groupchat. * Sends an IQ stanza to the XMPP server to retract a message in this groupchat.
* @private * @private
* @method _converse.ChatRoom#sendRetractionIQ * @method _converse.ChatRoom#sendRetractionIQ
* @param { _converse.Message } message - The message which we're retracting. * @param { _converse.ChatRoomMessage } message - The message which we're retracting.
* @param { string } [reason] - The reason for retracting the message. * @param { string } [reason] - The reason for retracting the message.
*/ */
sendRetractionIQ (message, reason) { sendRetractionIQ (message, reason) {
@ -1243,7 +1244,7 @@ const ChatRoomMixin = {
* 'roomconfig' data. * 'roomconfig' data.
* @private * @private
* @method _converse.ChatRoom#autoConfigureChatRoom * @method _converse.ChatRoom#autoConfigureChatRoom
* @returns { Promise<XMLElement> } * @returns { Promise<Element> }
* Returns a promise which resolves once a response IQ has * Returns a promise which resolves once a response IQ has
* been received. * been received.
*/ */
@ -1262,7 +1263,7 @@ const ChatRoomMixin = {
* has been received. * has been received.
* @private * @private
* @method _converse.ChatRoom#fetchRoomConfiguration * @method _converse.ChatRoom#fetchRoomConfiguration
* @returns { Promise<XMLElement> } * @returns { Promise<Element> }
*/ */
fetchRoomConfiguration () { fetchRoomConfiguration () {
return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER })); return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER }));
@ -1273,7 +1274,7 @@ const ChatRoomMixin = {
* @private * @private
* @method _converse.ChatRoom#sendConfiguration * @method _converse.ChatRoom#sendConfiguration
* @param { Array } config - The groupchat configuration * @param { Array } config - The groupchat configuration
* @returns { Promise<XMLElement> } - A promise which resolves with * @returns { Promise<Element> } - A promise which resolves with
* the `result` stanza received from the XMPP server. * the `result` stanza received from the XMPP server.
*/ */
sendConfiguration (config = []) { sendConfiguration (config = []) {
@ -1715,7 +1716,7 @@ const ChatRoomMixin = {
* Given a presence stanza, update the occupant model based on its contents. * Given a presence stanza, update the occupant model based on its contents.
* @private * @private
* @method _converse.ChatRoom#updateOccupantsOnPresence * @method _converse.ChatRoom#updateOccupantsOnPresence
* @param { XMLElement } pres - The presence stanza * @param { Element } pres - The presence stanza
*/ */
updateOccupantsOnPresence (pres) { updateOccupantsOnPresence (pres) {
const data = parseMUCPresence(pres, this); const data = parseMUCPresence(pres, this);
@ -1902,7 +1903,7 @@ const ChatRoomMixin = {
* the `from` attribute. Doesn't check the `type` attribute. * the `from` attribute. Doesn't check the `type` attribute.
* @private * @private
* @method _converse.ChatRoom#isOwnMessage * @method _converse.ChatRoom#isOwnMessage
* @param { Object|XMLElement|_converse.Message } msg * @param { Object|Element|_converse.Message } msg
* @returns { boolean } * @returns { boolean }
*/ */
isOwnMessage (msg) { isOwnMessage (msg) {
@ -2150,7 +2151,7 @@ const ChatRoomMixin = {
}, },
/** /**
* @param {String} actor - The nickname of the actor that caused the notification * @param { String } actor - The nickname of the actor that caused the notification
* @param {String|Array<String>} states - The state or states representing the type of notificcation * @param {String|Array<String>} states - The state or states representing the type of notificcation
*/ */
removeNotification (actor, states) { removeNotification (actor, states) {
@ -2174,8 +2175,8 @@ const ChatRoomMixin = {
* *
* The state can be a XEP-0085 Chat State or a XEP-0045 join/leave * The state can be a XEP-0085 Chat State or a XEP-0045 join/leave
* state. * state.
* @param {String} actor - The nickname of the actor that causes the notification * @param { String } actor - The nickname of the actor that causes the notification
* @param {String} state - The state representing the type of notificcation * @param { String } state - The state representing the type of notificcation
*/ */
updateNotifications (actor, state) { updateNotifications (actor, state) {
const actors_per_state = this.notifications.toJSON(); const actors_per_state = this.notifications.toJSON();
@ -2223,7 +2224,7 @@ const ChatRoomMixin = {
/** /**
* Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info * Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info
* messages for them. * messages for them.
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
handleMEPNotification (attrs) { handleMEPNotification (attrs) {
if (attrs.from !== this.get('jid') || !attrs.activities) { if (attrs.from !== this.get('jid') || !attrs.activities) {
@ -2320,7 +2321,7 @@ const ChatRoomMixin = {
/** /**
* Handle a presence stanza that disconnects the user from the MUC * Handle a presence stanza that disconnects the user from the MUC
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
handleDisconnection (stanza) { handleDisconnection (stanza) {
const is_self = stanza.querySelector("status[code='110']") !== null; const is_self = stanza.querySelector("status[code='110']") !== null;
@ -2342,7 +2343,7 @@ const ChatRoomMixin = {
// each <x/> element pertains to a single user. // each <x/> element pertains to a single user.
const item = x.querySelector('item'); const item = x.querySelector('item');
const reason = item ? item.querySelector('reason')?.textContent : undefined; const reason = item ? item.querySelector('reason')?.textContent : undefined;
const actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined; const actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
const message = _converse.muc.disconnect_messages[codes[0]]; const message = _converse.muc.disconnect_messages[codes[0]];
const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED; const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
this.setDisconnectionState(message, reason, actor, status); this.setDisconnectionState(message, reason, actor, status);
@ -2455,7 +2456,7 @@ const ChatRoomMixin = {
* @private * @private
* @method _converse.ChatRoom#createInfoMessage * @method _converse.ChatRoom#createInfoMessage
* @param { string } code - The MUC status code * @param { string } code - The MUC status code
* @param { XMLElement } stanza - The original stanza that contains the code * @param { Element } stanza - The original stanza that contains the code
* @param { Boolean } is_self - Whether this stanza refers to our own presence * @param { Boolean } is_self - Whether this stanza refers to our own presence
*/ */
createInfoMessage (code, stanza, is_self) { createInfoMessage (code, stanza, is_self) {
@ -2498,7 +2499,7 @@ const ChatRoomMixin = {
* Create info messages based on a received presence or message stanza * Create info messages based on a received presence or message stanza
* @private * @private
* @method _converse.ChatRoom#createInfoMessages * @method _converse.ChatRoom#createInfoMessages
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
createInfoMessages (stanza) { createInfoMessages (stanza) {
const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code')); const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
@ -2517,7 +2518,7 @@ const ChatRoomMixin = {
* implied by) the server. * implied by) the server.
* @param { String } reason - The reason provided for the disconnection * @param { String } reason - The reason provided for the disconnection
* @param { String } actor - The person (if any) responsible for this disconnection * @param { String } actor - The person (if any) responsible for this disconnection
* @param { Integer } status - The status code (see `ROOMSTATUS`) * @param { number } status - The status code (see `ROOMSTATUS`)
*/ */
setDisconnectionState (message, reason, actor, status=ROOMSTATUS.DISCONNECTED) { setDisconnectionState (message, reason, actor, status=ROOMSTATUS.DISCONNECTED) {
this.session.save({ this.session.save({
@ -2554,7 +2555,7 @@ const ChatRoomMixin = {
* `connection_status` value for this {@link _converse.ChatRoom} as * `connection_status` value for this {@link _converse.ChatRoom} as
* well as any additional output that can be shown to the user. * well as any additional output that can be shown to the user.
* @private * @private
* @param { XMLElement } stanza - The presence stanza * @param { Element } stanza - The presence stanza
*/ */
onErrorPresence (stanza) { onErrorPresence (stanza) {
const __ = _converse.__; const __ = _converse.__;
@ -2619,7 +2620,7 @@ const ChatRoomMixin = {
* Listens for incoming presence stanzas from the service that hosts this MUC * Listens for incoming presence stanzas from the service that hosts this MUC
* @private * @private
* @method _converse.ChatRoom#onPresenceFromMUCHost * @method _converse.ChatRoom#onPresenceFromMUCHost
* @param { XMLElement } stanza - The presence stanza * @param { Element } stanza - The presence stanza
*/ */
onPresenceFromMUCHost (stanza) { onPresenceFromMUCHost (stanza) {
if (stanza.getAttribute('type') === 'error') { if (stanza.getAttribute('type') === 'error') {
@ -2638,7 +2639,7 @@ const ChatRoomMixin = {
* Handles incoming presence stanzas coming from the MUC * Handles incoming presence stanzas coming from the MUC
* @private * @private
* @method _converse.ChatRoom#onPresence * @method _converse.ChatRoom#onPresence
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
onPresence (stanza) { onPresence (stanza) {
if (stanza.getAttribute('type') === 'error') { if (stanza.getAttribute('type') === 'error') {
@ -2671,7 +2672,7 @@ const ChatRoomMixin = {
* user is the groupchat's owner. * user is the groupchat's owner.
* @private * @private
* @method _converse.ChatRoom#onOwnPresence * @method _converse.ChatRoom#onOwnPresence
* @param { XMLElement } pres - The stanza * @param { Element } pres - The stanza
*/ */
async onOwnPresence (stanza) { async onOwnPresence (stanza) {
await this.occupants.fetched; await this.occupants.fetched;

View File

@ -10,9 +10,9 @@ class ChatRoomOccupant extends Model {
defaults () { // eslint-disable-line class-methods-use-this defaults () { // eslint-disable-line class-methods-use-this
return { return {
'hats': [], hats: [],
'show': 'offline', show: 'offline',
'states': [] states: []
} }
} }

View File

@ -27,7 +27,7 @@ const { NS } = Strophe;
/** /**
* Parses a message stanza for XEP-0317 MEP notification data * Parses a message stanza for XEP-0317 MEP notification data
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Array } Returns an array of objects representing <activity> elements. * @returns { Array } Returns an array of objects representing <activity> elements.
*/ */
export function getMEPActivities (stanza) { export function getMEPActivities (stanza) {
@ -61,7 +61,7 @@ export function getMEPActivities (stanza) {
* Note, this function doesn't check whether this is actually a MAM archived stanza. * Note, this function doesn't check whether this is actually a MAM archived stanza.
* *
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Object } * @returns { Object }
*/ */
function getJIDFromMUCUserData (stanza) { function getJIDFromMUCUserData (stanza) {
@ -71,8 +71,8 @@ function getJIDFromMUCUserData (stanza) {
/** /**
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the * @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself. * message stanza, if it was contained, otherwise it's the message stanza itself.
* @returns { Object } * @returns { Object }
*/ */
@ -140,8 +140,8 @@ function getSender (attrs, chatbox) {
/** /**
* Parses a passed in message stanza and returns an object of attributes. * Parses a passed in message stanza and returns an object of attributes.
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the * @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself. * message stanza, if it was contained, otherwise it's the message stanza itself.
* @param { _converse.ChatRoom } chatbox * @param { _converse.ChatRoom } chatbox
* @param { _converse } _converse * @param { _converse } _converse
@ -342,7 +342,7 @@ export function parseMemberListIQ (iq) {
/** /**
* Parses a passed in MUC presence stanza and returns an object of attributes. * Parses a passed in MUC presence stanza and returns an object of attributes.
* @method parseMUCPresence * @method parseMUCPresence
* @param { XMLElement } stanza - The presence stanza * @param { Element } stanza - The presence stanza
* @param { _converse.ChatRoom } chatbox * @param { _converse.ChatRoom } chatbox
* @returns { MUCPresenceAttributes } * @returns { MUCPresenceAttributes }
*/ */

View File

@ -83,7 +83,14 @@ describe("A MUC occupant", function () {
await u.waitUntil(() => model.messages.length); await u.waitUntil(() => model.messages.length);
let message = model.messages.at(0); let message = model.messages.at(0);
expect(message.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd"); expect(message.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd");
expect(message.occupant).toBeUndefined(); expect(message.occupant).not.toBeUndefined();
let occupant = message.occupant;
expect(occupant.getDisplayName()).toBe('3rdwitch');
expect(occupant.get('nick')).toBe('3rdwitch');
expect(occupant.get('jid')).toBe(undefined);
expect(occupant.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd");
expect(message.getDisplayName()).toBe('3rdwitch'); expect(message.getDisplayName()).toBe('3rdwitch');
const presence = u.toStanza(` const presence = u.toStanza(`
@ -98,7 +105,7 @@ describe("A MUC occupant", function () {
</presence>`); </presence>`);
_converse.connection._dataRecv(mock.createRequest(presence)); _converse.connection._dataRecv(mock.createRequest(presence));
const occupant = await u.waitUntil(() => model.getOccupantByNickname('thirdwitch')); occupant = await u.waitUntil(() => model.getOccupantByNickname('thirdwitch'));
expect(occupant.get('occupant_id')).toBe('dd72603deec90a38ba552f7c68cbcc61bca202cd'); expect(occupant.get('occupant_id')).toBe('dd72603deec90a38ba552f7c68cbcc61bca202cd');
expect(model.occupants.findWhere({'occupant_id': "dd72603deec90a38ba552f7c68cbcc61bca202cd"})).toBe(occupant); expect(model.occupants.findWhere({'occupant_id': "dd72603deec90a38ba552f7c68cbcc61bca202cd"})).toBe(occupant);

View File

@ -85,10 +85,9 @@ export async function openChatRoom (jid, settings) {
* See XEP-0249: Direct MUC invitations. * See XEP-0249: Direct MUC invitations.
* @private * @private
* @method _converse.ChatRoom#onDirectMUCInvitation * @method _converse.ChatRoom#onDirectMUCInvitation
* @param { XMLElement } message - The message stanza containing the invitation. * @param { Element } message - The message stanza containing the invitation.
*/ */
export async function onDirectMUCInvitation (message) { export async function onDirectMUCInvitation (message) {
const { __ } = _converse;
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(), const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
from = Strophe.getBareJidFromJid(message.getAttribute('from')), from = Strophe.getBareJidFromJid(message.getAttribute('from')),
room_jid = x_el.getAttribute('jid'), room_jid = x_el.getAttribute('jid'),
@ -99,21 +98,20 @@ export async function onDirectMUCInvitation (message) {
result = true; result = true;
} else { } else {
// Invite request might come from someone not your roster list // Invite request might come from someone not your roster list
let contact = _converse.roster.get(from); const contact = _converse.roster.get(from)?.getDisplayName() ?? from;
contact = contact ? contact.getDisplayName() : from;
if (!reason) { /**
result = await api.confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid)); * *Hook* which is used to gather confirmation whether a direct MUC
} else { * invitation should be accepted or not.
result = await api.confirm( *
__( * It's meant for consumers of `@converse/headless` to subscribe to
'%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"', * this hook and then ask the user to confirm.
contact, *
room_jid, * @event _converse#confirmDirectMUCInvitation
reason */
) result = await api.hook('confirmDirectMUCInvitation', { contact, reason, jid: room_jid }, false);
);
}
} }
if (result) { if (result) {
const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') }); const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) { if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {

View File

@ -10,7 +10,7 @@ export default {
* @method api.ping * @method api.ping
* @param { String } [jid] - The JID of the service to ping * @param { String } [jid] - The JID of the service to ping
* If the ping is sent out to the user's bare JID and no response is received it will attempt to reconnect. * If the ping is sent out to the user's bare JID and no response is received it will attempt to reconnect.
* @param { Integer } [timeout] - The amount of time in * @param { number } [timeout] - The amount of time in
* milliseconds to wait for a response. The default is 10000; * milliseconds to wait for a response. The default is 10000;
* @returns { Boolean | null } * @returns { Boolean | null }
* Whether the pinged entity responded with a non-error IQ stanza. * Whether the pinged entity responded with a non-error IQ stanza.

View File

@ -32,12 +32,12 @@ converse.plugins.add('converse-pubsub', {
* Publshes an item to a PubSub node * Publshes an item to a PubSub node
* *
* @method _converse.api.pubsub.publish * @method _converse.api.pubsub.publish
* @param {string} jid The JID of the pubsub service where the node resides. * @param { string } jid The JID of the pubsub service where the node resides.
* @param {string} node The node being published to * @param { string } node The node being published to
* @param {Strophe.Builder} item The Strophe.Builder representation of the XML element being published * @param {Strophe.Builder} item The Strophe.Builder representation of the XML element being published
* @param {object} options An object representing the publisher options * @param { object } options An object representing the publisher options
* (see https://xmpp.org/extensions/xep-0060.html#publisher-publish-options) * (see https://xmpp.org/extensions/xep-0060.html#publisher-publish-options)
* @param {boolean} strict_options Indicates whether the publisher * @param { boolean } strict_options Indicates whether the publisher
* options are a strict requirement or not. If they're NOT * options are a strict requirement or not. If they're NOT
* strict, then Converse will publish to the node even if * strict, then Converse will publish to the node even if
* the publish options precondication cannot be met. * the publish options precondication cannot be met.

View File

@ -55,8 +55,8 @@ export default {
* Add a contact. * Add a contact.
* *
* @method _converse.api.contacts.add * @method _converse.api.contacts.add
* @param {string} jid The JID of the contact to be added * @param { string } jid The JID of the contact to be added
* @param {string} [name] A custom name to show the user by in the roster * @param { string } [name] A custom name to show the user by in the roster
* @example * @example
* _converse.api.contacts.add('buddy@example.com') * _converse.api.contacts.add('buddy@example.com')
* @example * @example

View File

@ -1,3 +1,4 @@
import '@converse/headless/plugins/status/api.js';
import { Model } from '@converse/skeletor/src/model.js'; import { Model } from '@converse/skeletor/src/model.js';
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';
@ -93,21 +94,12 @@ const RosterContact = Model.extend({
/** /**
* Send a presence subscription request to this roster contact * Send a presence subscription request to this roster contact
* @private
* @method _converse.RosterContacts#subscribe * @method _converse.RosterContacts#subscribe
* @param { String } message - An optional message to explain the * @param { String } message - An optional message to explain the
* reason for the subscription request. * reason for the subscription request.
*/ */
subscribe (message) { subscribe (message) {
const pres = $pres({to: this.get('jid'), type: "subscribe"}); api.user.presence.send('subscribe', this.get('jid'), message);
if (message && message !== "") {
pres.c("status").t(message).up();
}
const nick = _converse.xmppstatus.getNickname() || _converse.xmppstatus.getFullname();
if (nick) {
pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
}
api.send(pres);
this.save('ask', "subscribe"); // ask === 'subscribe' Means we have asked to subscribe to them. this.save('ask', "subscribe"); // ask === 'subscribe' Means we have asked to subscribe to them.
return this; return this;
}, },
@ -135,7 +127,6 @@ const RosterContact = Model.extend({
* send notification of the subscription state change to the user. * send notification of the subscription state change to the user.
* @private * @private
* @method _converse.RosterContacts#ackUnsubscribe * @method _converse.RosterContacts#ackUnsubscribe
* @param { String } jid - The Jabber ID of the user who is unsubscribing
*/ */
ackUnsubscribe () { ackUnsubscribe () {
api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')})); api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));

View File

@ -112,7 +112,7 @@ const RosterContacts = Collection.extend({
* @method _converse.RosterContacts#addAndSubscribe * @method _converse.RosterContacts#addAndSubscribe
* @param { String } jid - The Jabber ID of the user being added and subscribed to. * @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @param { String } name - The name of that user * @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to * @param { Array<String> } groups - Any roster groups the user might belong to
* @param { String } message - An optional message to explain the reason for the subscription request. * @param { String } message - An optional message to explain the reason for the subscription request.
* @param { Object } attributes - Any additional attributes to be stored on the user's model. * @param { Object } attributes - Any additional attributes to be stored on the user's model.
*/ */
@ -128,9 +128,7 @@ const RosterContacts = Collection.extend({
* @method _converse.RosterContacts#sendContactAddIQ * @method _converse.RosterContacts#sendContactAddIQ
* @param { String } jid - The Jabber ID of the user being added * @param { String } jid - The Jabber ID of the user being added
* @param { String } name - The name of that user * @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to * @param { Array<String> } groups - Any roster groups the user might belong to
* @param { Function } callback - A function to call once the IQ is returned
* @param { Function } errback - A function to call if an error occurred
*/ */
sendContactAddIQ (jid, name, groups) { sendContactAddIQ (jid, name, groups) {
name = name ? name : null; name = name ? name : null;
@ -148,7 +146,7 @@ const RosterContacts = Collection.extend({
* @method _converse.RosterContacts#addContactToRoster * @method _converse.RosterContacts#addContactToRoster
* @param { String } jid - The Jabber ID of the user being added and subscribed to. * @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @param { String } name - The name of that user * @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to * @param { Array<String> } groups - Any roster groups the user might belong to
* @param { Object } attributes - Any additional attributes to be stored on the user's model. * @param { Object } attributes - Any additional attributes to be stored on the user's model.
*/ */
async addContactToRoster (jid, name, groups, attributes) { async addContactToRoster (jid, name, groups, attributes) {
@ -190,7 +188,7 @@ const RosterContacts = Collection.extend({
* Handle roster updates from the XMPP server. * Handle roster updates from the XMPP server.
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push * See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
* @method _converse.RosterContacts#onRosterPush * @method _converse.RosterContacts#onRosterPush
* @param { XMLElement } IQ - The IQ stanza received from the XMPP server. * @param { Element } iq - The IQ stanza received from the XMPP server.
*/ */
onRosterPush (iq) { onRosterPush (iq) {
const id = iq.getAttribute('id'); const id = iq.getAttribute('id');
@ -226,7 +224,7 @@ const RosterContacts = Collection.extend({
/** /**
* When the roster receives a push event from server (i.e. new entry in your contacts roster). * When the roster receives a push event from server (i.e. new entry in your contacts roster).
* @event _converse#rosterPush * @event _converse#rosterPush
* @type { XMLElement } * @type { Element }
* @example _converse.api.listen.on('rosterPush', iq => { ... }); * @example _converse.api.listen.on('rosterPush', iq => { ... });
*/ */
api.trigger('rosterPush', iq); api.trigger('rosterPush', iq);
@ -279,7 +277,7 @@ const RosterContacts = Collection.extend({
* See also the `cachedRoster` event further up, which gets called instead of * See also the `cachedRoster` event further up, which gets called instead of
* `roster` if its already in `sessionStorage`. * `roster` if its already in `sessionStorage`.
* @event _converse#roster * @event _converse#roster
* @type { XMLElement } * @type { Element }
* @example _converse.api.listen.on('roster', iq => { ... }); * @example _converse.api.listen.on('roster', iq => { ... });
* @example _converse.api.waitUntil('roster').then(iq => { ... }); * @example _converse.api.waitUntil('roster').then(iq => { ... });
*/ */
@ -289,7 +287,7 @@ const RosterContacts = Collection.extend({
/** /**
* Update or create RosterContact models based on the given `item` XML * Update or create RosterContact models based on the given `item` XML
* node received in the resulting IQ stanza from the server. * node received in the resulting IQ stanza from the server.
* @param { XMLElement } item * @param { Element } item
*/ */
updateContact (item) { updateContact (item) {
const jid = item.getAttribute('jid'); const jid = item.getAttribute('jid');
@ -379,9 +377,7 @@ const RosterContacts = Collection.extend({
_converse.xmppstatus.save({'status': show}, {'silent': true}); _converse.xmppstatus.save({'status': show}, {'silent': true});
const status_message = presence.querySelector('status')?.textContent; const status_message = presence.querySelector('status')?.textContent;
if (status_message) { if (status_message) _converse.xmppstatus.save({ status_message });
_converse.xmppstatus.save({'status_message': status_message});
}
} }
if (_converse.jid === jid && presence_type === 'unavailable') { if (_converse.jid === jid && presence_type === 'unavailable') {
// XXX: We've received an "unavailable" presence from our // XXX: We've received an "unavailable" presence from our
@ -414,11 +410,11 @@ const RosterContacts = Collection.extend({
return; // Ignore MUC return; // Ignore MUC
} }
const status_message = presence.querySelector('status')?.textContent;
const contact = this.get(bare_jid); const contact = this.get(bare_jid);
if (contact && (status_message !== contact.get('status'))) { if (contact) {
contact.save({'status': status_message}); const status = presence.querySelector('status')?.textContent;
if (contact.get('status') !== status) contact.save({status});
} }
if (presence_type === 'subscribed' && contact) { if (presence_type === 'subscribed' && contact) {

View File

@ -30,7 +30,7 @@ export const Presence = Model.extend({
const hpr = this.getHighestPriorityResource(); const hpr = this.getHighestPriorityResource();
const show = hpr?.attributes?.show || 'offline'; const show = hpr?.attributes?.show || 'offline';
if (this.get('show') !== show) { if (this.get('show') !== show) {
this.save({'show': show}); this.save({ show });
} }
}, },
@ -48,16 +48,16 @@ export const Presence = Model.extend({
* from the passed in presence stanza. * from the passed in presence stanza.
* Also updates the presence if the resource has higher priority (and is newer). * Also updates the presence if the resource has higher priority (and is newer).
* @private * @private
* @param { XMLElement } presence: The presence stanza * @param { Element } presence: The presence stanza
*/ */
addResource (presence) { addResource (presence) {
const jid = presence.getAttribute('from'), const jid = presence.getAttribute('from');
name = Strophe.getResourceFromJid(jid), const name = Strophe.getResourceFromJid(jid);
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop(), const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop();
priority = presence.querySelector('priority')?.textContent ?? 0, const priority = presence.querySelector('priority')?.textContent;
resource = this.resources.get(name), const resource = this.resources.get(name);
settings = { const settings = {
'name': name, name,
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10), 'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
'show': presence.querySelector('show')?.textContent ?? 'online', 'show': presence.querySelector('show')?.textContent ?? 'online',
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString() 'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
@ -78,9 +78,7 @@ export const Presence = Model.extend({
*/ */
removeResource (name) { removeResource (name) {
const resource = this.resources.get(name); const resource = this.resources.get(name);
if (resource) { resource?.destroy();
resource.destroy();
}
} }
}); });

View File

@ -1,8 +1,10 @@
import log from "@converse/headless/log"; import log from "@converse/headless/log";
import { Model } from '@converse/skeletor/src/model.js'; import { Model } from '@converse/skeletor/src/model.js';
import { RosterFilter } from '@converse/headless/plugins/roster/filter.js'; import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
import { STATUS_WEIGHTS } from "../../shared/constants";
import { _converse, api, converse } from "@converse/headless/core"; import { _converse, api, converse } from "@converse/headless/core";
import { initStorage } from '@converse/headless/utils/storage.js'; import { initStorage } from '@converse/headless/utils/storage.js';
import { shouldClearCache } from '@converse/headless/utils/core.js';
const { $pres } = converse.env; const { $pres } = converse.env;
@ -88,7 +90,7 @@ async function clearPresences () {
*/ */
export async function onClearSession () { export async function onClearSession () {
await clearPresences(); await clearPresences();
if (_converse.shouldClearCache()) { if (shouldClearCache()) {
if (_converse.rostergroups) { if (_converse.rostergroups) {
await _converse.rostergroups.clearStore(); await _converse.rostergroups.clearStore();
delete _converse.rostergroups; delete _converse.rostergroups;
@ -197,12 +199,12 @@ export function rejectPresenceSubscription (jid, message) {
export function contactsComparator (contact1, contact2) { export function contactsComparator (contact1, contact2) {
const status1 = contact1.presence.get('show') || 'offline'; const status1 = contact1.presence.get('show') || 'offline';
const status2 = contact2.presence.get('show') || 'offline'; const status2 = contact2.presence.get('show') || 'offline';
if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) { if (STATUS_WEIGHTS[status1] === STATUS_WEIGHTS[status2]) {
const name1 = (contact1.getDisplayName()).toLowerCase(); const name1 = (contact1.getDisplayName()).toLowerCase();
const name2 = (contact2.getDisplayName()).toLowerCase(); const name2 = (contact2.getDisplayName()).toLowerCase();
return name1 < name2 ? -1 : (name1 > name2? 1 : 0); return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
} else { } else {
return _converse.STATUS_WEIGHTS[status1] < _converse.STATUS_WEIGHTS[status2] ? -1 : 1; return STATUS_WEIGHTS[status1] < STATUS_WEIGHTS[status2] ? -1 : 1;
} }
} }

View File

@ -1,38 +1,8 @@
import { _converse, api } from '@converse/headless/core'; import { _converse, api } from '../../core';
import { STATUS_WEIGHTS } from '../../shared/constants';
export default { export default {
/**
* @namespace _converse.api.user.presence
* @memberOf _converse.api.user
*/
presence: {
/**
* Send out a presence stanza
* @method _converse.api.user.presence.send
* @param { String } type
* @param { String } to
* @param { String } [status] - An optional status message
* @param { Element[]|Strophe.Builder[]|Element|Strophe.Builder } [child_nodes]
* Nodes(s) to be added as child nodes of the `presence` XML element.
*/
async send (type, to, status, child_nodes) {
await api.waitUntil('statusInitialized');
if (child_nodes && !Array.isArray(child_nodes)) {
child_nodes = [child_nodes];
}
const model = _converse.xmppstatus
const presence = await model.constructPresence(type, to, status);
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
api.send(presence);
if (['away', 'chat', 'dnd', 'online', 'xa', undefined].includes(type)) {
const mucs = await api.rooms.get();
mucs.forEach(muc => muc.sendStatusPresence(type, status, child_nodes));
}
}
},
/** /**
* Set and get the user's chat status, also called their *availability*. * Set and get the user's chat status, also called their *availability*.
* @namespace _converse.api.user.status * @namespace _converse.api.user.status
@ -55,15 +25,15 @@ export default {
* *
* @async * @async
* @method _converse.api.user.status.set * @method _converse.api.user.status.set
* @param {string} value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa') * @param { string } value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
* @param {string} [message] A custom status message * @param { string } [message] A custom status message
* *
* @example _converse.api.user.status.set('dnd'); * @example _converse.api.user.status.set('dnd');
* @example _converse.api.user.status.set('dnd', 'In a meeting'); * @example _converse.api.user.status.set('dnd', 'In a meeting');
*/ */
async set (value, message) { async set (value, message) {
const data = {'status': value}; const data = {'status': value};
if (!Object.keys(_converse.STATUS_WEIGHTS).includes(value)) { if (!Object.keys(STATUS_WEIGHTS).includes(value)) {
throw new Error( throw new Error(
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1' 'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
); );
@ -85,7 +55,7 @@ export default {
/** /**
* @async * @async
* @method _converse.api.user.status.message.get * @method _converse.api.user.status.message.get
* @returns {string} The status message * @returns { Promise<string> } The status message
* @example const message = _converse.api.user.status.message.get() * @example const message = _converse.api.user.status.message.get()
*/ */
async get () { async get () {
@ -95,7 +65,7 @@ export default {
/** /**
* @async * @async
* @method _converse.api.user.status.message.set * @method _converse.api.user.status.message.set
* @param {string} status The status message * @param { string } status The status message
* @example _converse.api.user.status.message.set('In a meeting'); * @example _converse.api.user.status.message.set('In a meeting');
*/ */
async set (status) { async set (status) {

View File

@ -5,6 +5,7 @@
import XMPPStatus from './status.js'; import XMPPStatus from './status.js';
import status_api from './api.js'; import status_api from './api.js';
import { _converse, api, converse } from '@converse/headless/core'; import { _converse, api, converse } from '@converse/headless/core';
import { shouldClearCache } from '@converse/headless/utils/core.js';
import { import {
addStatusToMUCJoinPresence, addStatusToMUCJoinPresence,
initStatus, initStatus,
@ -52,7 +53,7 @@ converse.plugins.add('converse-status', {
}); });
api.listen.on('clearSession', () => { api.listen.on('clearSession', () => {
if (_converse.shouldClearCache() && _converse.xmppstatus) { if (shouldClearCache() && _converse.xmppstatus) {
_converse.xmppstatus.destroy(); _converse.xmppstatus.destroy();
delete _converse.xmppstatus; delete _converse.xmppstatus;
api.promises.add(['statusInitialized']); api.promises.add(['statusInitialized']);

View File

@ -5,10 +5,11 @@ import { _converse, api, converse } from '@converse/headless/core';
const { Strophe, $pres } = converse.env; const { Strophe, $pres } = converse.env;
const XMPPStatus = Model.extend({ export default class XMPPStatus extends Model {
defaults () {
defaults () { // eslint-disable-line class-methods-use-this
return { "status": api.settings.get("default_state") } return { "status": api.settings.get("default_state") }
}, }
initialize () { initialize () {
this.on('change', item => { this.on('change', item => {
@ -19,54 +20,72 @@ const XMPPStatus = Model.extend({
api.user.presence.send(this.get('status'), null, this.get('status_message')); api.user.presence.send(this.get('status'), null, this.get('status_message'));
} }
}); });
}, }
getNickname () { getDisplayName () {
return this.getFullname() || this.getNickname() || _converse.bare_jid;
}
getNickname () { // eslint-disable-line class-methods-use-this
return api.settings.get('nickname'); return api.settings.get('nickname');
}, }
getFullname () { getFullname () { // eslint-disable-line class-methods-use-this
// Gets overridden in converse-vcard return ''; // Gets overridden in converse-vcard
return ''; }
},
/** Constructs a presence stanza
* @param { string } [type]
* @param { string } [to] - The JID to which this presence should be sent
* @param { string } [status_message]
*/
async constructPresence (type, to=null, status_message) { async constructPresence (type, to=null, status_message) {
type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state")); type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));
status_message = typeof status_message === 'string' ? status_message : this.get('status_message'); status_message = typeof status_message === 'string' ? status_message : this.get('status_message');
let presence; let presence;
const attrs = {to};
if ((type === 'unavailable') || if (type === 'subscribe') {
presence = $pres({ to, type });
const { xmppstatus } = _converse;
const nick = xmppstatus.getNickname();
if (nick) presence.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
} else if ((type === 'unavailable') ||
(type === 'probe') || (type === 'probe') ||
(type === 'error') || (type === 'error') ||
(type === 'unsubscribe') || (type === 'unsubscribe') ||
(type === 'unsubscribed') || (type === 'unsubscribed') ||
(type === 'subscribe') ||
(type === 'subscribed')) { (type === 'subscribed')) {
attrs['type'] = type; presence = $pres({ to, type });
presence = $pres(attrs);
} else if (type === 'offline') { } else if (type === 'offline') {
attrs['type'] = 'unavailable'; presence = $pres({ to, type: 'unavailable' });
presence = $pres(attrs);
} else if (type === 'online') { } else if (type === 'online') {
presence = $pres(attrs); presence = $pres({ to });
} else { } else {
presence = $pres(attrs).c('show').t(type).up(); presence = $pres({ to }).c('show').t(type).up();
} }
if (status_message) { if (status_message) presence.c('status').t(status_message).up();
presence.c('status').t(status_message).up();
}
const priority = api.settings.get("priority"); const priority = api.settings.get("priority");
presence.c('priority').t(isNaN(Number(priority)) ? 0 : priority).up(); presence.c('priority').t(isNaN(Number(priority)) ? 0 : priority).up();
if (_converse.idle) {
const { idle, idle_seconds } = _converse;
if (idle) {
const idle_since = new Date(); const idle_since = new Date();
idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds); idle_since.setSeconds(idle_since.getSeconds() - idle_seconds);
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()}); presence.c('idle', { xmlns: Strophe.NS.IDLE, since: idle_since.toISOString() });
} }
/**
* *Hook* which allows plugins to modify a presence stanza
* @event _converse#constructedPresence
*/
presence = await api.hook('constructedPresence', null, presence); presence = await api.hook('constructedPresence', null, presence);
return presence; return presence;
} }
}); }
export default XMPPStatus;

View File

@ -22,8 +22,8 @@ export default {
* for the passed in JID. * for the passed in JID.
* *
* @method _converse.api.vcard.set * @method _converse.api.vcard.set
* @param {string} jid The JID for which the VCard should be set * @param { string } jid The JID for which the VCard should be set
* @param {object} data A map of VCard keys and values * @param { object } data A map of VCard keys and values
* @example * @example
* let jid = _converse.bare_jid; * let jid = _converse.bare_jid;
* _converse.api.vcard.set( jid, { * _converse.api.vcard.set( jid, {
@ -67,7 +67,7 @@ export default {
* @param {Model|string} model Either a `Model` instance, or a string JID. * @param {Model|string} model Either a `Model` instance, or a string JID.
* If a `Model` instance is passed in, then it must have either a `jid` * If a `Model` instance is passed in, then it must have either a `jid`
* attribute or a `muc_jid` attribute. * attribute or a `muc_jid` attribute.
* @param {boolean} [force] A boolean indicating whether the vcard should be * @param { boolean } [force] A boolean indicating whether the vcard should be
* fetched from the server even if it's been fetched before. * fetched from the server even if it's been fetched before.
* @returns {promise} A Promise which resolves with the VCard data for a particular JID or for * @returns {promise} A Promise which resolves with the VCard data for a particular JID or for
* a `Model` instance which represents an entity with a JID (such as a roster contact, * a `Model` instance which represents an entity with a JID (such as a roster contact,
@ -106,8 +106,8 @@ export default {
* returned VCard data. * returned VCard data.
* *
* @method _converse.api.vcard.update * @method _converse.api.vcard.update
* @param {Model} model A `Model` instance * @param { Model } model A `Model` instance
* @param {boolean} [force] A boolean indicating whether the vcard should be * @param { boolean } [force] A boolean indicating whether the vcard should be
* fetched again even if it's been fetched before. * fetched again even if it's been fetched before.
* @returns {promise} A promise which resolves once the update has completed. * @returns {promise} A promise which resolves once the update has completed.
* @example * @example

View File

@ -1,6 +1,7 @@
import log from "@converse/headless/log"; import log from "@converse/headless/log";
import { _converse, api, converse } from "../../core.js"; import { _converse, api, converse } from "../../core.js";
import { initStorage } from '@converse/headless/utils/storage.js'; import { initStorage } from '@converse/headless/utils/storage.js';
import { shouldClearCache } from '@converse/headless/utils/core.js';
const { Strophe, $iq, u } = converse.env; const { Strophe, $iq, u } = converse.env;
@ -163,7 +164,7 @@ export async function initVCardCollection () {
export function clearVCardsSession () { export function clearVCardsSession () {
if (_converse.shouldClearCache()) { if (shouldClearCache()) {
api.promises.add('VCardsInitialized'); api.promises.add('VCardsInitialized');
if (_converse.vcards) { if (_converse.vcards) {
_converse.vcards.clearStore(); _converse.vcards.clearStore();

View File

@ -1,11 +1,36 @@
import i18n from './i18n.js'; import i18n from './i18n.js';
import log from '../log.js'; import log from '../log.js';
import { CONNECTION_STATUS } from './constants'; import pluggable from 'pluggable.js/src/pluggable.js';
import { Events } from '@converse/skeletor/src/events.js';
import { Router } from '@converse/skeletor/src/router.js'; import { Router } from '@converse/skeletor/src/router.js';
import { TimeoutError } from './errors.js';
import { createStore, getDefaultStore } from '../utils/storage.js'; import { createStore, getDefaultStore } from '../utils/storage.js';
import { getInitSettings } from './settings/utils.js'; import { getInitSettings } from './settings/utils.js';
import { getOpenPromise } from '@converse/openpromise'; import { getOpenPromise } from '@converse/openpromise';
import { shouldClearCache } from '../utils/core.js';
import {
ACTIVE,
ANONYMOUS,
CHATROOMS_TYPE,
CLOSED,
COMPOSING,
CONTROLBOX_TYPE,
DEFAULT_IMAGE,
DEFAULT_IMAGE_TYPE,
EXTERNAL,
FAILURE,
GONE,
HEADLINES_TYPE,
INACTIVE,
LOGIN,
LOGOUT,
OPENED,
PAUSED,
PREBIND,
PRIVATE_CHAT_TYPE,
SUCCESS,
VERSION_NAME
} from './constants';
/** /**
@ -16,67 +41,51 @@ import { getOpenPromise } from '@converse/openpromise';
*/ */
const _converse = { const _converse = {
log, log,
CONNECTION_STATUS,
shouldClearCache, // TODO: Should be moved to utils with next major release
VERSION_NAME,
templates: {}, templates: {},
promises: { promises: {
'initialized': getOpenPromise() 'initialized': getOpenPromise()
}, },
STATUS_WEIGHTS: { // TODO: remove constants in next major release
'offline': 6, ANONYMOUS,
'unavailable': 5, CLOSED,
'xa': 4, EXTERNAL,
'away': 3, LOGIN,
'dnd': 2, LOGOUT,
'chat': 1, // We currently don't differentiate between "chat" and "online" OPENED,
'online': 1 PREBIND,
},
ANONYMOUS: 'anonymous',
CLOSED: 'closed',
EXTERNAL: 'external',
LOGIN: 'login',
LOGOUT: 'logout',
OPENED: 'opened',
PREBIND: 'prebind',
/** SUCCESS,
* @constant FAILURE,
* @type { integer }
*/
STANZA_TIMEOUT: 20000,
SUCCESS: 'success', DEFAULT_IMAGE_TYPE,
FAILURE: 'failure', DEFAULT_IMAGE,
// Generated from css/images/user.svg INACTIVE,
DEFAULT_IMAGE_TYPE: 'image/svg+xml', ACTIVE,
DEFAULT_IMAGE: "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCI+CiA8cmVjdCB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgZmlsbD0iIzU1NSIvPgogPGNpcmNsZSBjeD0iNjQiIGN5PSI0MSIgcj0iMjQiIGZpbGw9IiNmZmYiLz4KIDxwYXRoIGQ9Im0yOC41IDExMiB2LTEyIGMwLTEyIDEwLTI0IDI0LTI0IGgyMyBjMTQgMCAyNCAxMiAyNCAyNCB2MTIiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==", COMPOSING,
PAUSED,
GONE,
PRIVATE_CHAT_TYPE,
CHATROOMS_TYPE,
HEADLINES_TYPE,
CONTROLBOX_TYPE,
TIMEOUTS: {
// Set as module attr so that we can override in tests. // Set as module attr so that we can override in tests.
// TODO: replace with config settings
TIMEOUTS: {
PAUSED: 10000, PAUSED: 10000,
INACTIVE: 90000 INACTIVE: 90000
}, },
// XEP-0085 Chat states
// https://xmpp.org/extensions/xep-0085.html
INACTIVE: 'inactive',
ACTIVE: 'active',
COMPOSING: 'composing',
PAUSED: 'paused',
GONE: 'gone',
// Chat types
PRIVATE_CHAT_TYPE: 'chatbox',
CHATROOMS_TYPE: 'chatroom',
HEADLINES_TYPE: 'headline',
CONTROLBOX_TYPE: 'controlbox',
default_connection_options: {'explicitResourceBinding': true}, default_connection_options: {'explicitResourceBinding': true},
router: new Router(), router: new Router(),
TimeoutError: TimeoutError,
isTestEnv: () => { isTestEnv: () => {
return getInitSettings()['bosh_service_url'] === 'montague.lit/http-bind'; return getInitSettings()['bosh_service_url'] === 'montague.lit/http-bind';
}, },
@ -112,4 +121,10 @@ const _converse = {
'___': str => str '___': str => str
} }
// Make _converse an event emitter
Object.assign(_converse, Events);
// Make _converse pluggable
pluggable.enable(_converse, '_converse', 'pluggable');
export default _converse; export default _converse;

View File

@ -0,0 +1,132 @@
import _converse from '../_converse.js';
import isFunction from '../../utils/core.js';
export default {
/**
* Lets you trigger events, which can be listened to via
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
*
* Some events also double as promises and can be waited on via {@link _converse.api.waitUntil}.
*
* @method _converse.api.trigger
* @param { string } name - The event name
* @param {...any} [argument] - Argument to be passed to the event handler
* @param { object } [options]
* @param { boolean } [options.synchronous] - Whether the event is synchronous or not.
* When a synchronous event is fired, a promise will be returned
* by {@link _converse.api.trigger} which resolves once all the
* event handlers' promises have been resolved.
*/
async trigger (name) {
if (!_converse._events) {
return;
}
const args = Array.from(arguments);
const options = args.pop();
if (options && options.synchronous) {
const events = _converse._events[name] || [];
const event_args = args.splice(1);
await Promise.all(events.map(e => e.callback.apply(e.ctx, event_args)));
} else {
_converse.trigger.apply(_converse, arguments);
}
const promise = _converse.promises[name];
if (promise !== undefined) {
promise.resolve();
}
},
/**
* Triggers a hook which can be intercepted by registered listeners via
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}.
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
* A hook is a special kind of event which allows you to intercept a data
* structure in order to modify it, before passing it back.
* @async
* @param { string } name - The hook name
* @param {...any} context - The context to which the hook applies (could be for example, a {@link _converse.ChatBox})).
* @param {...any} data - The data structure to be intercepted and modified by the hook listeners.
* @returns {Promise<any>} - A promise that resolves with the modified data structure.
*/
hook (name, context, data) {
const events = _converse._events[name] || [];
if (events.length) {
// Create a chain of promises, with each one feeding its output to
// the next. The first input is a promise with the original data
// sent to this hook.
return events.reduce((o, e) => o.then(d => e.callback(context, d)), Promise.resolve(data));
} else {
return data;
}
},
/**
* Converse emits events to which you can subscribe to.
*
* The `listen` namespace exposes methods for creating event listeners
* (aka handlers) for these events.
*
* @namespace _converse.api.listen
* @memberOf _converse
*/
listen: {
/**
* Lets you listen to an event exactly once.
* @method _converse.api.listen.once
* @param { string } name The event's name
* @param { function } callback The callback method to be called when the event is emitted.
* @param { object } [context] The value of the `this` parameter for the callback.
* @example _converse.api.listen.once('message', function (messageXML) { ... });
*/
once: _converse.once.bind(_converse),
/**
* Lets you subscribe to an event.
* Every time the event fires, the callback method specified by `callback` will be called.
* @method _converse.api.listen.on
* @param { string } name The event's name
* @param { function } callback The callback method to be called when the event is emitted.
* @param { object } [context] The value of the `this` parameter for the callback.
* @example _converse.api.listen.on('message', function (messageXML) { ... });
*/
on: _converse.on.bind(_converse),
/**
* To stop listening to an event, you can use the `not` method.
* @method _converse.api.listen.not
* @param { string } name The event's name
* @param { function } callback The callback method that is to no longer be called when the event fires
* @example _converse.api.listen.not('message', function (messageXML);
*/
not: _converse.off.bind(_converse),
/**
* Subscribe to an incoming stanza
* Every a matched stanza is received, the callback method specified by
* `callback` will be called.
* @method _converse.api.listen.stanza
* @param { string } name The stanza's name
* @param { object } options Matching options (e.g. 'ns' for namespace, 'type' for stanza type, also 'id' and 'from');
* @param { function } handler The callback method to be called when the stanza appears
*/
stanza (name, options, handler) {
if (isFunction(options)) {
handler = options;
options = {};
} else {
options = options || {};
}
_converse.connection.addHandler(
handler,
options.ns,
name,
options.type,
options.id,
options.from,
options
);
}
},
}

View File

@ -0,0 +1,31 @@
import _converse from '../_converse.js';
import connection_api from '../connection/api.js';
import events_api from '../api/events.js';
import promise_api from '../api/promise.js';
import send_api from '../api/send.js';
import user_api from '../api/user.js';
import { settings_api } from '../settings/api.js';
/**
* ### The private API
*
* The private API methods are only accessible via the closured {@link _converse}
* object, which is only available to plugins.
*
* These methods are kept private (i.e. not global) because they may return
* sensitive data which should be kept off-limits to other 3rd-party scripts
* that might be running in the page.
*
* @namespace _converse.api
* @memberOf _converse
*/
const api = _converse.api = {
connection: connection_api,
settings: settings_api,
...send_api,
...user_api,
...events_api,
...promise_api,
};
export default api;

View File

@ -0,0 +1,35 @@
import { _converse, api } from '../../core.js';
export default {
/**
* @namespace _converse.api.user.presence
* @memberOf _converse.api.user
*/
presence: {
/**
* Send out a presence stanza
* @method _converse.api.user.presence.send
* @param { String } [type]
* @param { String } [to]
* @param { String } [status] - An optional status message
* @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
* Nodes(s) to be added as child nodes of the `presence` XML element.
*/
async send (type, to, status, child_nodes) {
await api.waitUntil('statusInitialized');
if (child_nodes && !Array.isArray(child_nodes)) {
child_nodes = [child_nodes];
}
const model = _converse.xmppstatus
const presence = await model.constructPresence(type, to, status);
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
api.send(presence);
if (['away', 'chat', 'dnd', 'online', 'xa', undefined].includes(type)) {
const mucs = await api.rooms.get();
mucs.forEach(muc => muc.sendStatusPresence(type, status, child_nodes));
}
}
}
}

View File

@ -0,0 +1,78 @@
import _converse from '@converse/headless/shared/_converse.js';
import { getOpenPromise } from '@converse/openpromise';
import { waitUntil, isFunction } from '../../utils/core.js';
export default {
/**
* Converse and its plugins trigger various events which you can listen to via the
* {@link _converse.api.listen} namespace.
*
* Some of these events are also available as [ES2015 Promises](http://es6-features.org/#PromiseUsage)
* although not all of them could logically act as promises, since some events
* might be fired multpile times whereas promises are to be resolved (or
* rejected) only once.
*
* Events which are also promises include:
*
* * [cachedRoster](/docs/html/events.html#cachedroster)
* * [chatBoxesFetched](/docs/html/events.html#chatBoxesFetched)
* * [pluginsInitialized](/docs/html/events.html#pluginsInitialized)
* * [roster](/docs/html/events.html#roster)
* * [rosterContactsFetched](/docs/html/events.html#rosterContactsFetched)
* * [rosterGroupsFetched](/docs/html/events.html#rosterGroupsFetched)
* * [rosterInitialized](/docs/html/events.html#rosterInitialized)
*
* The various plugins might also provide promises, and they do this by using the
* `promises.add` api method.
*
* @namespace _converse.api.promises
* @memberOf _converse.api
*/
promises: {
/**
* By calling `promises.add`, a new [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* is made available for other code or plugins to depend on via the
* {@link _converse.api.waitUntil} method.
*
* Generally, it's the responsibility of the plugin which adds the promise to
* also resolve it.
*
* This is done by calling {@link _converse.api.trigger}, which not only resolves the
* promise, but also emits an event with the same name (which can be listened to
* via {@link _converse.api.listen}).
*
* @method _converse.api.promises.add
* @param {string|array} [name|names] The name or an array of names for the promise(s) to be added
* @param { boolean } [replace=true] Whether this promise should be replaced with a new one when the user logs out.
* @example _converse.api.promises.add('foo-completed');
*/
add (promises, replace=true) {
promises = Array.isArray(promises) ? promises : [promises];
promises.forEach(name => {
const promise = getOpenPromise();
promise.replace = replace;
_converse.promises[name] = promise;
});
}
},
/**
* Wait until a promise is resolved or until the passed in function returns
* a truthy value.
* @method _converse.api.waitUntil
* @param {string|function} condition - The name of the promise to wait for,
* or a function which should eventually return a truthy value.
* @returns {Promise}
*/
waitUntil (condition) {
if (isFunction(condition)) {
return waitUntil(condition);
} else {
const promise = _converse.promises[condition];
if (promise === undefined) {
return null;
}
return promise;
}
},
}

View File

@ -0,0 +1,212 @@
import ConnectionFeedback from './../connection/feedback.js';
import URI from 'urijs';
import _converse from '../_converse.js';
import dayjs from 'dayjs';
import i18n from '../i18n';
import log from '../../log.js';
import sizzle from 'sizzle';
import u, { setUnloadEvent } from '../../utils/core.js';
import { ANONYMOUS, CHAT_STATES, KEYCODES, VERSION_NAME } from '../constants.js';
import { Collection } from "@converse/skeletor/src/collection";
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
import { TimeoutError } from '../errors.js';
import { filesize } from 'filesize';
import { html } from 'lit';
import { initAppSettings } from '../settings/utils.js';
import { sprintf } from 'sprintf-js';
import { stx } from '../../utils/stanza.js';
import {
cleanup,
initClientConfig,
initPlugins,
initSessionStorage,
registerGlobalEventHandlers,
} from '../../utils/init.js';
/**
* ### The Public API
*
* This namespace contains public API methods which are are
* accessible on the global `converse` object.
* They are public, because any JavaScript in the
* page can call them. Public methods therefore dont expose any sensitive
* or closured data. To do that, youll need to create a plugin, which has
* access to the private API method.
*
* @global
* @namespace converse
*/
export const converse = Object.assign(window.converse || {}, {
CHAT_STATES,
keycodes: KEYCODES,
/**
* Public API method which initializes Converse.
* This method must always be called when using Converse.
* @async
* @memberOf converse
* @method initialize
* @param { object } config A map of [configuration-settings](https://conversejs.org/docs/html/configuration.html#configuration-settings).
* @example
* converse.initialize({
* auto_list_rooms: false,
* auto_subscribe: false,
* bosh_service_url: 'https://bind.example.com',
* hide_muc_server: false,
* i18n: 'en',
* play_sounds: true,
* show_controlbox_by_default: true,
* debug: false,
* roster_groups: true
* });
*/
async initialize (settings) {
const { api } = _converse;
await cleanup(_converse);
setUnloadEvent();
initAppSettings(settings);
_converse.strict_plugin_dependencies = settings.strict_plugin_dependencies; // Needed by pluggable.js
log.setLogLevel(api.settings.get("loglevel"));
if (api.settings.get("authentication") === ANONYMOUS) {
if (api.settings.get("auto_login") && !api.settings.get('jid')) {
throw new Error("Config Error: you need to provide the server's " +
"domain via the 'jid' option when using anonymous " +
"authentication with auto_login.");
}
}
_converse.router.route(
/^converse\?loglevel=(debug|info|warn|error|fatal)$/, 'loglevel',
l => log.setLogLevel(l)
);
_converse.connfeedback = new ConnectionFeedback();
/* When reloading the page:
* For new sessions, we need to send out a presence stanza to notify
* the server/network that we're online.
* When re-attaching to an existing session we don't need to again send out a presence stanza,
* because it's as if "we never left" (see onConnectStatusChanged).
* https://github.com/conversejs/converse.js/issues/521
*/
_converse.send_initial_presence = true;
await initSessionStorage(_converse);
await initClientConfig(_converse);
await i18n.initialize();
initPlugins(_converse);
// Register all custom elements
// XXX: api.elements is defined in the UI part of Converse, outside of @converse/headless.
// This line should probably be moved to the UI code as part of a larger refactoring.
api.elements?.register();
registerGlobalEventHandlers(_converse);
try {
!History.started && _converse.router.history.start();
} catch (e) {
log.error(e);
}
const plugins = _converse.pluggable.plugins
if (api.settings.get("auto_login") || api.settings.get("keepalive") && plugins['converse-bosh']?.enabled()) {
await api.user.login(null, null, true);
}
/**
* Triggered once converse.initialize has finished.
* @event _converse#initialized
*/
api.trigger('initialized');
if (_converse.isTestEnv()) {
return _converse;
}
},
/**
* Exposes methods for adding and removing plugins. You'll need to write a plugin
* if you want to have access to the private API methods defined further down below.
*
* For more information on plugins, read the documentation on [writing a plugin](/docs/html/plugin_development.html).
* @namespace plugins
* @memberOf converse
*/
plugins: {
/**
* Registers a new plugin.
* @method converse.plugins.add
* @param { string } name The name of the plugin
* @param { object } plugin The plugin object
* @example
* const plugin = {
* initialize: function () {
* // Gets called as soon as the plugin has been loaded.
*
* // Inside this method, you have access to the private
* // API via `_covnerse.api`.
*
* // The private _converse object contains the core logic
* // and data-structures of Converse.
* }
* }
* converse.plugins.add('myplugin', plugin);
*/
add (name, plugin) {
plugin.__name__ = name;
if (_converse.pluggable.plugins[name] !== undefined) {
throw new TypeError(
`Error: plugin with name "${name}" has already been ` + 'registered!'
);
} else {
_converse.pluggable.plugins[name] = plugin;
}
}
},
/**
* Utility methods and globals from bundled 3rd party libraries.
* @typedef ConverseEnv
* @property { Error } converse.env.TimeoutError
* @property { function } converse.env.$build - Creates a Strophe.Builder, for creating stanza objects.
* @property { function } converse.env.$iq - Creates a Strophe.Builder with an <iq/> element as the root.
* @property { function } converse.env.$msg - Creates a Strophe.Builder with an <message/> element as the root.
* @property { function } converse.env.$pres - Creates a Strophe.Builder with an <presence/> element as the root.
* @property { function } converse.env.Promise - The Promise implementation used by Converse.
* @property { function } converse.env.Strophe - The [Strophe](http://strophe.im/strophejs) XMPP library used by Converse.
* @property { function } converse.env.f - And instance of Lodash with its methods wrapped to produce immutable auto-curried iteratee-first data-last methods.
* @property { function } converse.env.sizzle - [Sizzle](https://sizzlejs.com) CSS selector engine.
* @property { function } converse.env.sprintf
* @property { object } converse.env._ - The instance of [lodash-es](http://lodash.com) used by Converse.
* @property { object } converse.env.dayjs - [DayJS](https://github.com/iamkun/dayjs) date manipulation library.
* @property { object } converse.env.utils - Module containing common utility methods used by Converse.
* @memberOf converse
*/
'env': {
$build,
$iq,
$msg,
$pres,
'utils': u,
Collection,
Model,
Promise,
Strophe,
TimeoutError,
URI,
VERSION_NAME,
dayjs,
filesize,
html,
log,
sizzle,
sprintf,
stx,
u,
}
});

View File

@ -0,0 +1,81 @@
import _converse from '../../shared/_converse.js';
import log from '../../log.js';
import { Strophe } from 'strophe.js/src/strophe';
import { TimeoutError } from '../errors.js';
import { toStanza } from '../../utils/stanza.js';
export default {
/**
* Allows you to send XML stanzas.
* @method _converse.api.send
* @param { Element | Stanza } stanza
* @return { void }
* @example
* const msg = converse.env.$msg({
* 'from': 'juliet@example.com/balcony',
* 'to': 'romeo@example.net',
* 'type':'chat'
* });
* _converse.api.send(msg);
*/
send (stanza) {
const { api } = _converse;
if (!api.connection.connected()) {
log.warn("Not sending stanza because we're not connected!");
log.warn(Strophe.serialize(stanza));
return;
}
if (typeof stanza === 'string') {
stanza = toStanza(stanza);
} else if (stanza?.tree) {
stanza = stanza.tree();
}
if (stanza.tagName === 'iq') {
return api.sendIQ(stanza);
} else {
_converse.connection.send(stanza);
api.trigger('send', stanza);
}
},
/**
* Send an IQ stanza
* @method _converse.api.sendIQ
* @param { Element } stanza
* @param { number } [timeout] - The default timeout value is taken from
* the `stanza_timeout` configuration setting.
* @param { Boolean } [reject=true] - Whether an error IQ should cause the promise
* to be rejected. If `false`, the promise will resolve instead of being rejected.
* @returns { Promise } A promise which resolves (or potentially rejected) once we
* receive a `result` or `error` stanza or once a timeout is reached.
* If the IQ stanza being sent is of type `result` or `error`, there's
* nothing to wait for, so an already resolved promise is returned.
*/
sendIQ (stanza, timeout, reject=true) {
const { api, connection } = _converse;
let promise;
stanza = stanza.tree?.() ?? stanza;
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
timeout = timeout || api.settings.get('stanza_timeout');
if (reject) {
promise = new Promise((resolve, reject) => connection.sendIQ(stanza, resolve, reject, timeout));
promise.catch((e) => {
if (e === null) {
throw new TimeoutError(
`Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
);
}
});
} else {
promise = new Promise((resolve) => connection.sendIQ(stanza, resolve, resolve, timeout));
}
} else {
_converse.connection.sendIQ(stanza);
promise = Promise.resolve();
}
api.trigger('send', stanza);
return promise;
}
}

View File

@ -0,0 +1,114 @@
import _converse from '../_converse.js';
import presence_api from './presence.js';
import u, { replacePromise } from '../../utils/core.js';
import { attemptNonPreboundSession, initConnection, setUserJID } from '../../utils/init.js';
import { getOpenPromise } from '@converse/openpromise';
import { user_settings_api } from '../settings/api.js';
import { LOGOUT, PREBIND } from '../constants.js';
export default {
/**
* This grouping collects API functions related to the current logged in user.
*
* @namespace _converse.api.user
* @memberOf _converse.api
*/
user: {
settings: user_settings_api,
...presence_api,
/**
* @method _converse.api.user.jid
* @returns {string} The current user's full JID (Jabber ID)
* @example _converse.api.user.jid())
*/
jid () {
return _converse.connection.jid;
},
/**
* Logs the user in.
*
* If called without any parameters, Converse will try
* to log the user in by calling the `prebind_url` or `credentials_url` depending
* on whether prebinding is used or not.
*
* @method _converse.api.user.login
* @param { string } [jid]
* @param { string } [password]
* @param { boolean } [automatic=false] - An internally used flag that indicates whether
* this method was called automatically once the connection has been
* initialized. It's used together with the `auto_login` configuration flag
* to determine whether Converse should try to log the user in if it
* fails to restore a previous auth'd session.
* @returns { Promise<void> }
*/
async login (jid, password, automatic=false) {
const { api } = _converse;
jid = jid || api.settings.get('jid');
if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
initConnection();
}
if (api.settings.get("connection_options")?.worker && (await _converse.connection.restoreWorkerSession())) {
return;
}
if (jid) {
jid = await setUserJID(jid);
}
// See whether there is a BOSH session to re-attach to
const bosh_plugin = _converse.pluggable.plugins['converse-bosh'];
if (bosh_plugin?.enabled()) {
if (await _converse.restoreBOSHSession()) {
return;
} else if (api.settings.get("authentication") === PREBIND && (!automatic || api.settings.get("auto_login"))) {
return _converse.startNewPreboundBOSHSession();
}
}
password = password || api.settings.get("password");
const credentials = (jid && password) ? { jid, password } : null;
attemptNonPreboundSession(credentials, automatic);
},
/**
* Logs the user out of the current XMPP session.
* @method _converse.api.user.logout
* @example _converse.api.user.logout();
*/
async logout () {
const { api } = _converse;
/**
* Triggered before the user is logged out
* @event _converse#beforeLogout
*/
await api.trigger('beforeLogout', {'synchronous': true});
const promise = getOpenPromise();
const complete = () => {
// Recreate all the promises
Object.keys(_converse.promises).forEach(replacePromise);
delete _converse.jid
// Remove the session JID, otherwise the user would just be logged
// in again upon reload. See #2759
localStorage.removeItem('conversejs-session-jid');
/**
* Triggered once the user has logged out.
* @event _converse#logout
*/
api.trigger('logout');
promise.resolve();
}
_converse.connection.setDisconnectionCause(LOGOUT, undefined, true);
if (_converse.connection !== undefined) {
api.listen.once('disconnected', () => complete());
_converse.connection.disconnect();
} else {
complete();
}
return promise;
}
}
}

View File

@ -0,0 +1,15 @@
import _converse from '../_converse';
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe';
export default Model.extend({
defaults: {
'connection_status': Strophe.Status.DISCONNECTED,
'message': ''
},
initialize () {
const { api } = _converse;
this.on('change', () => api.trigger('connfeedback', _converse.connfeedback));
}
});

View File

@ -1,11 +1,12 @@
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import log from "../../log.js"; import log from "../../log.js";
import sizzle from 'sizzle'; import sizzle from 'sizzle';
import { BOSH_WAIT } from '../../shared/constants.js'; import { ANONYMOUS, BOSH_WAIT, LOGOUT } from '../../shared/constants.js';
import { CONNECTION_STATUS } from '../constants';
import { Strophe } from 'strophe.js/src/core.js'; import { Strophe } from 'strophe.js/src/core.js';
import { _converse, api } from "../../core.js"; import { _converse, api } from "../../core.js";
import { getOpenPromise } from '@converse/openpromise';
import { clearSession, tearDown } from "../../utils/core.js"; import { clearSession, tearDown } from "../../utils/core.js";
import { getOpenPromise } from '@converse/openpromise';
import { setUserJID, } from '../../utils/init.js'; import { setUserJID, } from '../../utils/init.js';
const i = Object.keys(Strophe.Status).reduce((max, k) => Math.max(max, Strophe.Status[k]), 0); const i = Object.keys(Strophe.Status).reduce((max, k) => Math.max(max, Strophe.Status[k]), 0);
@ -127,7 +128,7 @@ export class Connection extends Strophe.Connection {
this._proto = new Strophe.Bosh(this); this._proto = new Strophe.Bosh(this);
this.service = api.settings.get('bosh_service_url'); this.service = api.settings.get('bosh_service_url');
} else if (api.connection.isType('bosh') && api.settings.get("websocket_url")) { } else if (api.connection.isType('bosh') && api.settings.get("websocket_url")) {
if (api.settings.get("authentication") === _converse.ANONYMOUS) { if (api.settings.get("authentication") === ANONYMOUS) {
// When reconnecting anonymously, we need to connect with only // When reconnecting anonymously, we need to connect with only
// the domain, not the full JID that we had in our previous // the domain, not the full JID that we had in our previous
// (now failed) session. // (now failed) session.
@ -149,7 +150,7 @@ export class Connection extends Strophe.Connection {
const conn_status = _converse.connfeedback.get('connection_status'); const conn_status = _converse.connfeedback.get('connection_status');
if (conn_status === Strophe.Status.CONNFAIL) { if (conn_status === Strophe.Status.CONNFAIL) {
this.switchTransport(); this.switchTransport();
} else if (conn_status === Strophe.Status.AUTHFAIL && api.settings.get("authentication") === _converse.ANONYMOUS) { } else if (conn_status === Strophe.Status.AUTHFAIL && api.settings.get("authentication") === ANONYMOUS) {
// When reconnecting anonymously, we need to connect with only // When reconnecting anonymously, we need to connect with only
// the domain, not the full JID that we had in our previous // the domain, not the full JID that we had in our previous
// (now failed) session. // (now failed) session.
@ -163,7 +164,7 @@ export class Connection extends Strophe.Connection {
*/ */
api.trigger('will-reconnect'); api.trigger('will-reconnect');
if (api.settings.get("authentication") === _converse.ANONYMOUS) { if (api.settings.get("authentication") === ANONYMOUS) {
await clearSession(); await clearSession();
} }
return api.user.login(); return api.user.login();
@ -264,7 +265,7 @@ export class Connection extends Strophe.Connection {
if (api.settings.get("auto_reconnect")) { if (api.settings.get("auto_reconnect")) {
const reason = this.disconnection_reason; const reason = this.disconnection_reason;
if (this.disconnection_cause === Strophe.Status.AUTHFAIL) { if (this.disconnection_cause === Strophe.Status.AUTHFAIL) {
if (api.settings.get("credentials_url") || api.settings.get("authentication") === _converse.ANONYMOUS) { if (api.settings.get("credentials_url") || api.settings.get("authentication") === ANONYMOUS) {
// If `credentials_url` is set, we reconnect, because we might // If `credentials_url` is set, we reconnect, because we might
// be receiving expirable tokens from the credentials_url. // be receiving expirable tokens from the credentials_url.
// //
@ -286,7 +287,7 @@ export class Connection extends Strophe.Connection {
); );
return this.finishDisconnection(); return this.finishDisconnection();
} else if ( } else if (
this.disconnection_cause === _converse.LOGOUT || this.disconnection_cause === LOGOUT ||
reason === Strophe.ErrorCondition.NO_AUTH_MECH || reason === Strophe.ErrorCondition.NO_AUTH_MECH ||
reason === "host-unknown" || reason === "host-unknown" ||
reason === "remote-connection-failed" reason === "remote-connection-failed"
@ -308,7 +309,7 @@ export class Connection extends Strophe.Connection {
*/ */
onConnectStatusChanged (status, message) { onConnectStatusChanged (status, message) {
const { __ } = _converse; const { __ } = _converse;
log.debug(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`); log.debug(`Status changed to: ${CONNECTION_STATUS[status]}`);
if (status === Strophe.Status.ATTACHFAIL) { if (status === Strophe.Status.ATTACHFAIL) {
this.setConnectionStatus(status); this.setConnectionStatus(status);
this.worker_attach_promise?.resolve(false); this.worker_attach_promise?.resolve(false);

View File

@ -1,6 +1,46 @@
import { Strophe } from 'strophe.js/src/strophe'; import { Strophe } from 'strophe.js/src/strophe';
export const BOSH_WAIT = 59; export const BOSH_WAIT = 59;
export const VERSION_NAME = "v10.1.6";
export const STATUS_WEIGHTS = {
offline: 6,
unavailable: 5,
xa: 4,
away: 3,
dnd: 2,
chat: 1, // We don't differentiate between "chat" and "online"
online: 1,
};
export const ANONYMOUS = 'anonymous';
export const CLOSED = 'closed';
export const EXTERNAL = 'external';
export const LOGIN = 'login';
export const LOGOUT = 'logout';
export const OPENED = 'opened';
export const PREBIND = 'prebind';
export const SUCCESS = 'success';
export const FAILURE = 'failure';
// Generated from css/images/user.svg
export const DEFAULT_IMAGE_TYPE = 'image/svg+xml';
export const DEFAULT_IMAGE =
'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCI+CiA8cmVjdCB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgZmlsbD0iIzU1NSIvPgogPGNpcmNsZSBjeD0iNjQiIGN5PSI0MSIgcj0iMjQiIGZpbGw9IiNmZmYiLz4KIDxwYXRoIGQ9Im0yOC41IDExMiB2LTEyIGMwLTEyIDEwLTI0IDI0LTI0IGgyMyBjMTQgMCAyNCAxMiAyNCAyNCB2MTIiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==';
// XEP-0085 Chat states
// https =//xmpp.org/extensions/xep-0085.html
export const INACTIVE = 'inactive';
export const ACTIVE = 'active';
export const COMPOSING = 'composing';
export const PAUSED = 'paused';
export const GONE = 'gone';
// Chat types
export const PRIVATE_CHAT_TYPE = 'chatbox';
export const CHATROOMS_TYPE = 'chatroom';
export const HEADLINES_TYPE = 'headline';
export const CONTROLBOX_TYPE = 'controlbox';
export const CONNECTION_STATUS = {}; export const CONNECTION_STATUS = {};
CONNECTION_STATUS[Strophe.Status.ATTACHED] = 'ATTACHED'; CONNECTION_STATUS[Strophe.Status.ATTACHED] = 'ATTACHED';
@ -15,6 +55,43 @@ CONNECTION_STATUS[Strophe.Status.ERROR] = 'ERROR';
CONNECTION_STATUS[Strophe.Status.RECONNECTING] = 'RECONNECTING'; CONNECTION_STATUS[Strophe.Status.RECONNECTING] = 'RECONNECTING';
CONNECTION_STATUS[Strophe.Status.REDIRECT] = 'REDIRECT'; CONNECTION_STATUS[Strophe.Status.REDIRECT] = 'REDIRECT';
// Add Strophe Namespaces
Strophe.addNamespace('ACTIVITY', 'http://jabber.org/protocol/activity');
Strophe.addNamespace('CARBONS', 'urn:xmpp:carbons:2');
Strophe.addNamespace('CHATSTATES', 'http://jabber.org/protocol/chatstates');
Strophe.addNamespace('CSI', 'urn:xmpp:csi:0');
Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
Strophe.addNamespace('EME', 'urn:xmpp:eme:0');
Strophe.addNamespace('FASTEN', 'urn:xmpp:fasten:0');
Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
Strophe.addNamespace('MENTIONS', 'urn:xmpp:mmn:0');
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
Strophe.addNamespace('MODERATE', 'urn:xmpp:message-moderate:0');
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
Strophe.addNamespace('OCCUPANTID', 'urn:xmpp:occupant-id:0');
Strophe.addNamespace('OMEMO', 'eu.siacs.conversations.axolotl');
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
Strophe.addNamespace('RAI', 'urn:xmpp:rai:0');
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
Strophe.addNamespace('REGISTER', 'jabber:iq:register');
Strophe.addNamespace('RETRACT', 'urn:xmpp:message-retract:0');
Strophe.addNamespace('ROSTERX', 'http://jabber.org/protocol/rosterx');
Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
Strophe.addNamespace('SID', 'urn:xmpp:sid:0');
Strophe.addNamespace('SPOILER', 'urn:xmpp:spoiler:0');
Strophe.addNamespace('STANZAS', 'urn:ietf:params:xml:ns:xmpp-stanzas');
Strophe.addNamespace('STYLING', 'urn:xmpp:styling:0');
Strophe.addNamespace('VCARD', 'vcard-temp');
Strophe.addNamespace('VCARDUPDATE', 'vcard-temp:x:update');
Strophe.addNamespace('XFORM', 'jabber:x:data');
Strophe.addNamespace('XHTML', 'http://www.w3.org/1999/xhtml');
// Core plugins are whitelisted automatically // Core plugins are whitelisted automatically
// These are just the @converse/headless plugins, for the full converse, // These are just the @converse/headless plugins, for the full converse,
// the other plugins are whitelisted in src/consts.js // the other plugins are whitelisted in src/consts.js
@ -35,7 +112,7 @@ export const CORE_PLUGINS = [
'converse-roster', 'converse-roster',
'converse-smacks', 'converse-smacks',
'converse-status', 'converse-status',
'converse-vcard' 'converse-vcard',
]; ];
export const URL_PARSE_OPTIONS = { 'start': /(\b|_)(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi }; export const URL_PARSE_OPTIONS = { 'start': /(\b|_)(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
@ -56,5 +133,5 @@ export const KEYCODES = {
FORWARD_SLASH: 47, FORWARD_SLASH: 47,
AT: 50, AT: 50,
META: 91, META: 91,
META_RIGHT: 93 META_RIGHT: 93,
} };

View File

@ -1,5 +1,5 @@
/** /**
* Custom error for indicating timeouts * Custom error for indicating timeouts
* @namespace _converse * @namespace converse.env
*/ */
export class TimeoutError extends Error {} export class TimeoutError extends Error {}

View File

@ -28,7 +28,7 @@ export class StanzaParseError extends Error {
* Extract the XEP-0359 stanza IDs from the passed in stanza * Extract the XEP-0359 stanza IDs from the passed in stanza
* and return a map containing them. * and return a map containing them.
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Object } * @returns { Object }
*/ */
export function getStanzaIDs (stanza, original_stanza) { export function getStanzaIDs (stanza, original_stanza) {
@ -72,8 +72,8 @@ export function getEncryptionAttributes (stanza) {
/** /**
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the * @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself. * message stanza, if it was contained, otherwise it's the message stanza itself.
* @returns { Object } * @returns { Object }
*/ */
@ -222,7 +222,7 @@ export function getOutOfBandAttributes (stanza) {
/** /**
* Returns the human readable error message contained in a `groupchat` message stanza of type `error`. * Returns the human readable error message contained in a `groupchat` message stanza of type `error`.
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
*/ */
export function getErrorAttributes (stanza) { export function getErrorAttributes (stanza) {
if (stanza.getAttribute('type') === 'error') { if (stanza.getAttribute('type') === 'error') {
@ -240,7 +240,7 @@ export function getErrorAttributes (stanza) {
/** /**
* Given a message stanza, find and return any XEP-0372 references * Given a message stanza, find and return any XEP-0372 references
* @param { XMLElement } stana - The message stanza * @param { Element } stana - The message stanza
* @returns { Reference } * @returns { Reference }
*/ */
export function getReferences (stanza) { export function getReferences (stanza) {
@ -279,7 +279,7 @@ export function getReceiptId (stanza) {
/** /**
* Determines whether the passed in stanza is a XEP-0280 Carbon * Determines whether the passed in stanza is a XEP-0280 Carbon
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Boolean } * @returns { Boolean }
*/ */
export function isCarbon (stanza) { export function isCarbon (stanza) {
@ -293,7 +293,7 @@ export function isCarbon (stanza) {
/** /**
* Returns the XEP-0085 chat state contained in a message stanza * Returns the XEP-0085 chat state contained in a message stanza
* @private * @private
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
*/ */
export function getChatState (stanza) { export function getChatState (stanza) {
return sizzle( return sizzle(
@ -319,7 +319,7 @@ export function isValidReceiptRequest (stanza, attrs) {
/** /**
* Check whether the passed-in stanza is a forwarded message that is "bare", * Check whether the passed-in stanza is a forwarded message that is "bare",
* i.e. it's not forwarded as part of a larger protocol, like MAM. * i.e. it's not forwarded as part of a larger protocol, like MAM.
* @param { XMLElement } stanza * @param { Element } stanza
*/ */
export function throwErrorIfInvalidForward (stanza) { export function throwErrorIfInvalidForward (stanza) {
const bare_forward = sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length; const bare_forward = sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length;
@ -334,7 +334,7 @@ export function throwErrorIfInvalidForward (stanza) {
* Determines whether the passed in stanza is a XEP-0333 Chat Marker * Determines whether the passed in stanza is a XEP-0333 Chat Marker
* @private * @private
* @method getChatMarker * @method getChatMarker
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Boolean } * @returns { Boolean }
*/ */
export function getChatMarker (stanza) { export function getChatMarker (stanza) {
@ -371,7 +371,7 @@ export function isServerMessage (stanza) {
* Determines whether the passed in stanza is a XEP-0313 MAM stanza * Determines whether the passed in stanza is a XEP-0313 MAM stanza
* @private * @private
* @method isArchived * @method isArchived
* @param { XMLElement } stanza - The message stanza * @param { Element } stanza - The message stanza
* @returns { Boolean } * @returns { Boolean }
*/ */
export function isArchived (original_stanza) { export function isArchived (original_stanza) {
@ -382,7 +382,7 @@ export function isArchived (original_stanza) {
/** /**
* Returns an object containing all attribute names and values for a particular element. * Returns an object containing all attribute names and values for a particular element.
* @method getAttributes * @method getAttributes
* @param { XMLElement } stanza * @param { Element } stanza
* @returns { Object } * @returns { Object }
*/ */
export function getAttributes (stanza) { export function getAttributes (stanza) {

View File

@ -19,8 +19,8 @@ Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
* [XEP-0059 RSM](https://xmpp.org/extensions/xep-0059.html) Attributes that can be used to filter query results * [XEP-0059 RSM](https://xmpp.org/extensions/xep-0059.html) Attributes that can be used to filter query results
* @property { String } [after] - The XEP-0359 stanza ID of a message after which messages should be returned. Implies forward paging. * @property { String } [after] - The XEP-0359 stanza ID of a message after which messages should be returned. Implies forward paging.
* @property { String } [before] - The XEP-0359 stanza ID of a message before which messages should be returned. Implies backward paging. * @property { String } [before] - The XEP-0359 stanza ID of a message before which messages should be returned. Implies backward paging.
* @property { Integer } [index=0] - The index of the results page to return. * @property { number } [index=0] - The index of the results page to return.
* @property { Integer } [max] - The maximum number of items to return. * @property { number } [max] - The maximum number of items to return.
*/ */
const RSM_QUERY_PARAMETERS = ['after', 'before', 'index', 'max']; const RSM_QUERY_PARAMETERS = ['after', 'before', 'index', 'max'];
@ -84,7 +84,7 @@ export class RSM {
* Returns a `<set>` XML element that confirms to XEP-0059 Result Set Management. * Returns a `<set>` XML element that confirms to XEP-0059 Result Set Management.
* The element is constructed based on the {@link module:converse-rsm~RSMQueryParameters} * The element is constructed based on the {@link module:converse-rsm~RSMQueryParameters}
* that are set on this RSM instance. * that are set on this RSM instance.
* @returns { XMLElement } * @returns { Element }
*/ */
toXML () { toXML () {
const xml = $build('set', {xmlns: Strophe.NS.RSM}); const xml = $build('set', {xmlns: Strophe.NS.RSM});

View File

@ -29,7 +29,7 @@ export const settings_api = {
* `converse.initialize`. * `converse.initialize`.
* *
* @method _converse.api.settings.extend * @method _converse.api.settings.extend
* @param {object} settings The configuration settings * @param { object } settings The configuration settings
* @example * @example
* _converse.api.settings.extend({ * _converse.api.settings.extend({
* 'enable_foo': true * 'enable_foo': true
@ -70,9 +70,10 @@ export const settings_api = {
* running and you want to change the configuration on-the-fly. * running and you want to change the configuration on-the-fly.
* *
* @method _converse.api.settings.set * @method _converse.api.settings.set
* @param {Object} [settings] An object containing configuration settings. * @param { Object | string } [settings_or_key]
* @param {string} [key] Alternatively to passing in an object, you can pass in a key and a value. * An object containing configuration settings.
* @param {string} [value] * 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);
* @example * @example
* _converse.api.settings.set({ * _converse.api.settings.set({
@ -80,8 +81,8 @@ export const settings_api = {
* "hide_offline_users": true * "hide_offline_users": true
* }); * });
*/ */
set (key, val) { set (settings_or_key, value) {
updateAppSettings(key, val); updateAppSettings(settings_or_key, value);
}, },
/** /**
@ -110,7 +111,7 @@ export const settings_api = {
* To stop listening to an event, you can use the `not` method. * To stop listening to an event, you can use the `not` method.
* @method _converse.api.settings.listen.not * @method _converse.api.settings.listen.not
* @param { String } name The event's name * @param { String } name The event's name
* @param { Function } callback The callback method that is to no longer be called when the event fires * @param { Function } handler The callback method that is to no longer be called when the event fires
* @example _converse.api.settings.listen.not('change', callback); * @example _converse.api.settings.listen.not('change', callback);
*/ */
not (name, handler) { not (name, handler) {
@ -142,7 +143,7 @@ export const user_settings_api = {
/** /**
* Get the value of a particular user setting. * Get the value of a particular user setting.
* @method _converse.api.user.settings.get * @method _converse.api.user.settings.get
* @param {String} key - The setting name * @param { String } key - The setting name
* @param {*} [fallback] - An optional fallback value if the user setting is undefined * @param {*} [fallback] - An optional fallback value if the user setting is undefined
* @returns {Promise} Promise which resolves with the value of the particular configuration setting. * @returns {Promise} Promise which resolves with the value of the particular configuration setting.
* @example _converse.api.user.settings.get("foo"); * @example _converse.api.user.settings.get("foo");
@ -156,9 +157,9 @@ export const user_settings_api = {
* Set one or many user settings. * Set one or many user settings.
* @async * @async
* @method _converse.api.user.settings.set * @method _converse.api.user.settings.set
* @param {Object} [settings] An object containing configuration settings. * @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 } [key] Alternatively to passing in an object, you can pass in a key and a value.
* @param {string} [value] * @param { string } [value]
* @example _converse.api.user.settings.set("foo", "bar"); * @example _converse.api.user.settings.set("foo", "bar");
* @example * @example
* _converse.api.user.settings.set({ * _converse.api.user.settings.set({

View File

@ -101,6 +101,7 @@ export const DEFAULT_SETTINGS = {
sid: undefined, sid: undefined,
singleton: false, singleton: false,
strict_plugin_dependencies: false, strict_plugin_dependencies: false,
stanza_timeout: 20000,
view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile' view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile'
websocket_url: undefined, websocket_url: undefined,
whitelisted_plugins: [], whitelisted_plugins: [],

View File

@ -8,14 +8,20 @@ import _converse from '@converse/headless/shared/_converse.js';
import compact from "lodash-es/compact"; import compact from "lodash-es/compact";
import isObject from "lodash-es/isObject"; import isObject from "lodash-es/isObject";
import last from "lodash-es/last"; import last from "lodash-es/last";
import log from '@converse/headless/log.js'; import log from '../log.js';
import sizzle from "sizzle"; import sizzle from "sizzle";
import { Model } from '@converse/skeletor/src/model.js'; import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe.js'; import { Strophe } from 'strophe.js/src/strophe.js';
import { getOpenPromise } from '@converse/openpromise'; import { getOpenPromise } from '@converse/openpromise';
import { settings_api } from '@converse/headless/shared/settings/api.js'; import { settings_api } from '../shared/settings/api.js';
import { stx , toStanza } from './stanza.js'; import { stx , toStanza } from './stanza.js';
/**
* The utils object
* @namespace u
*/
const u = {};
export function isElement (el) { export function isElement (el) {
return el instanceof Element || el instanceof HTMLDocument; return el instanceof Element || el instanceof HTMLDocument;
} }
@ -24,6 +30,10 @@ export function isError (obj) {
return Object.prototype.toString.call(obj) === "[object Error]"; return Object.prototype.toString.call(obj) === "[object Error]";
} }
export function isFunction (val) {
return typeof val === 'function';
}
export function isEmptyMessage (attrs) { export function isEmptyMessage (attrs) {
if (attrs instanceof Model) { if (attrs instanceof Model) {
attrs = attrs.attributes; attrs = attrs.attributes;
@ -35,7 +45,8 @@ export function isEmptyMessage (attrs) {
!attrs['body']; !attrs['body'];
} }
/* We distinguish between UniView and MultiView instances. /**
* We distinguish between UniView and MultiView instances.
* *
* UniView means that only one chat is visible, even though there might be multiple ongoing chats. * UniView means that only one chat is visible, even though there might be multiple ongoing chats.
* MultiView means that multiple chats may be visible simultaneously. * MultiView means that multiple chats may be visible simultaneously.
@ -44,16 +55,24 @@ export function isUniView () {
return ['mobile', 'fullscreen', 'embedded'].includes(settings_api.get("view_mode")); return ['mobile', 'fullscreen', 'embedded'].includes(settings_api.get("view_mode"));
} }
export function shouldClearCache () {
const { api } = _converse;
return !_converse.config.get('trusted') ||
api.settings.get('clear_cache_on_logout') ||
_converse.isTestEnv();
}
export async function tearDown () { export async function tearDown () {
await _converse.api.trigger('beforeTearDown', {'synchronous': true}); const { api } = _converse;
await api.trigger('beforeTearDown', {'synchronous': true});
window.removeEventListener('click', _converse.onUserActivity); window.removeEventListener('click', _converse.onUserActivity);
window.removeEventListener('focus', _converse.onUserActivity); window.removeEventListener('focus', _converse.onUserActivity);
window.removeEventListener('keypress', _converse.onUserActivity); window.removeEventListener('keypress', _converse.onUserActivity);
window.removeEventListener('mousemove', _converse.onUserActivity); window.removeEventListener('mousemove', _converse.onUserActivity);
window.removeEventListener(_converse.unloadevent, _converse.onUserActivity); window.removeEventListener(_converse.unloadevent, _converse.onUserActivity);
window.clearInterval(_converse.everySecondTrigger); window.clearInterval(_converse.everySecondTrigger);
_converse.api.trigger('afterTearDown'); api.trigger('afterTearDown');
return _converse; return _converse;
} }
@ -61,7 +80,7 @@ export async function tearDown () {
export function clearSession () { export function clearSession () {
_converse.session?.destroy(); _converse.session?.destroy();
delete _converse.session; delete _converse.session;
_converse.shouldClearCache() && _converse.api.user.settings.clear(); shouldClearCache() && _converse.api.user.settings.clear();
/** /**
* Synchronouse event triggered once the user session has been cleared, * Synchronouse event triggered once the user session has been cleared,
* for example when the user has logged out or when Converse has * for example when the user has logged out or when Converse has
@ -86,13 +105,6 @@ export function prefixMentions (message) {
return text; return text;
} }
/**
* The utils object
* @namespace u
*/
const u = {};
u.isTagEqual = function (stanza, name) { u.isTagEqual = function (stanza, name) {
if (stanza.tree?.()) { if (stanza.tree?.()) {
return u.isTagEqual(stanza.tree(), name); return u.isTagEqual(stanza.tree(), name);
@ -183,7 +195,7 @@ u.isChatRoom = function (model) {
return model && (model.get('type') === 'chatroom'); return model && (model.get('type') === 'chatroom');
} }
u.isErrorObject = function (o) { export function isErrorObject (o) {
return o instanceof Error; return o instanceof Error;
} }
@ -256,7 +268,7 @@ u.stringToElement = function (s) {
* Checks whether the DOM element matches the given selector. * Checks whether the DOM element matches the given selector.
* @private * @private
* @method u#matchesSelector * @method u#matchesSelector
* @param { DOMElement } el - The DOM element * @param { Element } el - The DOM element
* @param { String } selector - The selector * @param { String } selector - The selector
*/ */
u.matchesSelector = function (el, selector) { u.matchesSelector = function (el, selector) {
@ -275,7 +287,7 @@ u.matchesSelector = function (el, selector) {
* Returns a list of children of the DOM element that match the selector. * Returns a list of children of the DOM element that match the selector.
* @private * @private
* @method u#queryChildren * @method u#queryChildren
* @param { DOMElement } el - the DOM element * @param { Element } el - the DOM element
* @param { String } selector - the selector they should be matched against * @param { String } selector - the selector they should be matched against
*/ */
u.queryChildren = function (el, selector) { u.queryChildren = function (el, selector) {
@ -394,10 +406,10 @@ u.siblingIndex = function (el) {
/** /**
* Returns the current word being written in the input element * Returns the current word being written in the input element
* @method u#getCurrentWord * @method u#getCurrentWord
* @param {HTMLElement} input - The HTMLElement in which text is being entered * @param { HTMLElement } input - The HTMLElement in which text is being entered
* @param {integer} [index] - An optional rightmost boundary index. If given, the text * @param { number } [index] - An optional rightmost boundary index. If given, the text
* value of the input element will only be considered up until this index. * value of the input element will only be considered up until this index.
* @param {string} [delineator] - An optional string delineator to * @param { string } [delineator] - An optional string delineator to
* differentiate between words. * differentiate between words.
* @private * @private
*/ */
@ -477,8 +489,8 @@ export function getUniqueId (suffix) {
/** /**
* Clears the specified timeout and interval. * Clears the specified timeout and interval.
* @method u#clearTimers * @method u#clearTimers
* @param {number} timeout - Id if the timeout to clear. * @param { number } timeout - Id if the timeout to clear.
* @param {number} interval - Id of the interval to clear. * @param { number } interval - Id of the interval to clear.
* @private * @private
* @copyright Simen Bekkhus 2016 * @copyright Simen Bekkhus 2016
* @license MIT * @license MIT
@ -493,16 +505,16 @@ function clearTimers(timeout, interval) {
* Creates a {@link Promise} that resolves if the passed in function returns a truthy value. * Creates a {@link Promise} that resolves if the passed in function returns a truthy value.
* Rejects if it throws or does not return truthy within the given max_wait. * Rejects if it throws or does not return truthy within the given max_wait.
* @method u#waitUntil * @method u#waitUntil
* @param {Function} func - The function called every check_delay, * @param { Function } func - The function called every check_delay,
* and the result of which is the resolved value of the promise. * and the result of which is the resolved value of the promise.
* @param {number} [max_wait=300] - The time to wait before rejecting the promise. * @param { number } [max_wait=300] - The time to wait before rejecting the promise.
* @param {number} [check_delay=3] - The time to wait before each invocation of {func}. * @param { number } [check_delay=3] - The time to wait before each invocation of {func}.
* @returns {Promise} A promise resolved with the value of func, * @returns {Promise} A promise resolved with the value of func,
* or rejected with the exception thrown by it or it times out. * or rejected with the exception thrown by it or it times out.
* @copyright Simen Bekkhus 2016 * @copyright Simen Bekkhus 2016
* @license MIT * @license MIT
*/ */
u.waitUntil = function (func, max_wait=300, check_delay=3) { export function waitUntil (func, max_wait=300, check_delay=3) {
// Run the function once without setting up any listeners in case it's already true // Run the function once without setting up any listeners in case it's already true
try { try {
const result = func(); const result = func();
@ -620,6 +632,9 @@ export function saveWindowState (ev) {
export default Object.assign({ export default Object.assign({
shouldClearCache,
waitUntil, // TODO: remove. Only the API should be used
isErrorObject,
getRandomInt, getRandomInt,
getUniqueId, getUniqueId,
isElement, isElement,

View File

@ -13,7 +13,7 @@ const tplXformValue = (value) => `<value>${value}</value>`;
* Takes an HTML DOM and turns it into an XForm field. * Takes an HTML DOM and turns it into an XForm field.
* @private * @private
* @method u#webForm2xForm * @method u#webForm2xForm
* @param { DOMElement } field - the field to convert * @param { Element } field - the field to convert
*/ */
export function webForm2xForm (field) { export function webForm2xForm (field) {
const name = field.getAttribute('name'); const name = field.getAttribute('name');

View File

@ -4,7 +4,7 @@ import debounce from 'lodash-es/debounce';
import localDriver from 'localforage-webextensionstorage-driver/local'; import localDriver from 'localforage-webextensionstorage-driver/local';
import log from '../log.js'; import log from '../log.js';
import syncDriver from 'localforage-webextensionstorage-driver/sync'; import syncDriver from 'localforage-webextensionstorage-driver/sync';
import { CORE_PLUGINS } from '../shared/constants.js'; import { ANONYMOUS, CORE_PLUGINS, EXTERNAL, LOGIN, PREBIND } from '../shared/constants.js';
import { Connection, MockConnection } from '../shared/connection/index.js'; import { Connection, MockConnection } from '../shared/connection/index.js';
import { Model } from '@converse/skeletor/src/model.js'; import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe'; import { Strophe } from 'strophe.js/src/strophe';
@ -43,7 +43,7 @@ export function initConnection () {
const api = _converse.api; const api = _converse.api;
if (! api.settings.get('bosh_service_url')) { if (! api.settings.get('bosh_service_url')) {
if (api.settings.get("authentication") === _converse.PREBIND) { if (api.settings.get("authentication") === PREBIND) {
throw new Error("authentication is set to 'prebind' but we don't have a BOSH connection"); throw new Error("authentication is set to 'prebind' but we don't have a BOSH connection");
} }
} }
@ -182,7 +182,7 @@ function initPersistentStorage (_converse, store_name) {
function saveJIDtoSession (_converse, jid) { function saveJIDtoSession (_converse, jid) {
jid = _converse.session.get('jid') || jid; jid = _converse.session.get('jid') || jid;
if (_converse.api.settings.get("authentication") !== _converse.ANONYMOUS && !Strophe.getResourceFromJid(jid)) { if (_converse.api.settings.get("authentication") !== ANONYMOUS && !Strophe.getResourceFromJid(jid)) {
jid = jid.toLowerCase() + Connection.generateResource(); jid = jid.toLowerCase() + Connection.generateResource();
} }
_converse.jid = jid; _converse.jid = jid;
@ -357,7 +357,7 @@ async function getLoginCredentialsFromBrowser () {
if (!jid) return null; if (!jid) return null;
try { try {
const creds = await navigator.credentials.get({'password': true}); const creds = await navigator.credentials.get({ password: true});
if (creds && creds.type == 'password' && isValidJID(creds.id)) { if (creds && creds.type == 'password' && isValidJID(creds.id)) {
// XXX: We don't actually compare `creds.id` with `jid` because // XXX: We don't actually compare `creds.id` with `jid` because
// the user might have been presented a list of credentials with // the user might have been presented a list of credentials with
@ -387,7 +387,7 @@ async function getLoginCredentialsFromSCRAMKeys () {
export async function attemptNonPreboundSession (credentials, automatic) { export async function attemptNonPreboundSession (credentials, automatic) {
const { api } = _converse; const { api } = _converse;
if (api.settings.get("authentication") === _converse.LOGIN) { if (api.settings.get("authentication") === LOGIN) {
// XXX: If EITHER ``keepalive`` or ``auto_login`` is ``true`` and // XXX: 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,
// since we don't have a way to distinguish between wether we're // since we don't have a way to distinguish between wether we're
@ -417,7 +417,7 @@ export async function attemptNonPreboundSession (credentials, automatic) {
if (!_converse.isTestEnv()) log.warn("attemptNonPreboundSession: Couldn't find credentials to log in with"); if (!_converse.isTestEnv()) log.warn("attemptNonPreboundSession: Couldn't find credentials to log in with");
} else if ( } else if (
[_converse.ANONYMOUS, _converse.EXTERNAL].includes(api.settings.get("authentication")) && [ANONYMOUS, EXTERNAL].includes(api.settings.get("authentication")) &&
(!automatic || api.settings.get("auto_login")) (!automatic || api.settings.get("auto_login"))
) { ) {
connect(); connect();
@ -431,7 +431,7 @@ export async function attemptNonPreboundSession (credentials, automatic) {
* The user's plaintext password is not stored, nor any material from which * The user's plaintext password is not stored, nor any material from which
* the user's plaintext password could be recovered. * the user's plaintext password could be recovered.
* *
* @param { String } JID - The XMPP address for which to fetch the SCRAM keys * @param { String } jid - The XMPP address for which to fetch the SCRAM keys
* @returns { Promise } A promise which resolves once we've fetched the previously * @returns { Promise } A promise which resolves once we've fetched the previously
* used login keys. * used login keys.
*/ */
@ -444,9 +444,16 @@ export async function savedLoginInfo (jid) {
} }
/**
* @param { Object } [credentials]
* @param { string } credentials.password
* @param { Object } credentials.password
* @param { string } credentials.password.ck
* @returns { Promise<void> }
*/
async function connect (credentials) { async function connect (credentials) {
const { api } = _converse; const { api } = _converse;
if ([_converse.ANONYMOUS, _converse.EXTERNAL].includes(api.settings.get("authentication"))) { if ([ANONYMOUS, EXTERNAL].includes(api.settings.get("authentication"))) {
if (!_converse.jid) { if (!_converse.jid) {
throw new Error("Config Error: when using anonymous login " + throw new Error("Config Error: when using anonymous login " +
"you need to provide the server's domain via the 'jid' option. " + "you need to provide the server's domain via the 'jid' option. " +
@ -457,7 +464,7 @@ async function connect (credentials) {
_converse.connection.reset(); _converse.connection.reset();
} }
_converse.connection.connect(_converse.jid.toLowerCase()); _converse.connection.connect(_converse.jid.toLowerCase());
} else if (api.settings.get("authentication") === _converse.LOGIN) { } else if (api.settings.get("authentication") === LOGIN) {
const password = credentials?.password ?? (_converse.connection?.pass || api.settings.get("password")); const password = credentials?.password ?? (_converse.connection?.pass || api.settings.get("password"));
if (!password) { if (!password) {
if (api.settings.get("auto_login")) { if (api.settings.get("auto_login")) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More