Compare commits

...

170 Commits

Author SHA1 Message Date
pitchum a461444bed make version (instead of make release) 2023-10-08 13:53:45 +00:00
pitchum 1307f87912 Simplify chapril release process 2023-10-07 08:15:40 +00:00
pitchum 23cf858a02 Add some bash safeguards in README.charpil.md 2023-10-07 08:13:09 +00:00
pitchum b49146b36f [Branding] Customize homepage's text and links + CSS adjustments. 2023-10-07 08:13:09 +00:00
pitchum 8d67dce865 [Branding] Replace Converse's logo with Chapril's logo. 2023-10-07 08:13:07 +00:00
pitchum 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
JC Brand 0b3bcbfe40 Release 10.1.2 2023-02-17 11:56:30 +01:00
JC Brand 5e3139f563 Fixes #1490: Busy-loop when fetching registration form fails 2023-02-17 11:44:22 +01:00
JC Brand 7b8b32638c Fixes #3137
- Modernize the `RegisterPanel` component and turn it into a Lit element.
- Improve CSS and move into plugin.
- Fix button click handler not being registered.
- Fix switching between login/register form after logging out (Fixes #1556)
2023-02-16 23:45:59 +01:00
JC Brand 8035084e8e Remove unused dir 'converse-logs' 2023-02-16 14:48:02 +01:00
JC Brand 19466ddf62 Remove codefirst as sponsor 2023-02-16 14:47:23 +01:00
JC Brand bb7ed5315f XEP-0437 MUC RAI fixes
Avoid unnecessarily connecting to rooms that should instead receive room
activity indicators (RAI).

- Don't join a newly created room that qualifies for RAI.
- Upon switching back to the tab, don't reconnect to a room that qualifies for RAI.
- Don't ping a room that we didn't actually enter.
2023-02-16 14:25:24 +01:00
JC Brand 6719aeba45 Release 10.1.1 2023-02-15 15:14:13 +01:00
Igor 45844447bf Translated using Weblate (Russian)
Currently translated at 100.0% (534 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ru/
2023-02-15 15:06:56 +01:00
koukei 82314eb1df Translated using Weblate (Indonesian)
Currently translated at 76.4% (408 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/id/
2023-02-15 15:06:56 +01:00
nautilusx d9c86ed575 Translated using Weblate (German)
Currently translated at 99.8% (533 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/de/
2023-02-15 15:06:56 +01:00
Eric 89d231a5dd Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (534 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-02-15 15:06:56 +01:00
Oğuz Ersen 72406b4aac Translated using Weblate (Turkish)
Currently translated at 100.0% (534 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/tr/
2023-02-15 15:06:56 +01:00
gallegonovato e08b58c3d3 Translated using Weblate (Galician)
Currently translated at 100.0% (534 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/gl/
2023-02-15 15:06:56 +01:00
gallegonovato 387c992381 Translated using Weblate (Spanish)
Currently translated at 100.0% (534 of 534 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/es/
2023-02-15 15:06:56 +01:00
JC Brand 4e98383e65 Ran `npm update` 2023-02-15 15:00:38 +01:00
JC Brand dae84028c2 Fixes #1851: Sort open groupchats alphabetically 2023-02-15 14:43:56 +01:00
JC Brand 5e5bdc78ec Bump @converse/skeletor to 0.0.8 for @converse/headless 2023-02-15 14:43:39 +01:00
JC Brand 15c10376b0 Rename all templates to camelCase
To conform with naming conventions regarding functions, which the
templates are.
2023-02-15 14:29:07 +01:00
JC Brand ccc165facc Update roomslist and templates to bring in line with conventions 2023-02-15 14:29:07 +01:00
JC Brand aa299af3ec Update `files` in package.json to include Sass files in `./src` 2023-02-14 14:39:36 +01:00
JC Brand b16e3efbd4 Update to `@conversejs/skeletor` version 0.0.8 2023-02-14 13:25:40 +01:00
JC Brand 5db3e8ca51 Move adhoc plugin into own directory with separate files 2023-02-14 11:51:35 +01:00
JC Brand 0fcdb2a594 Add-hoc form fixes
- Provide actions as received in the Ad-Hoc form
- Add support for multi-stage ad-hoc forms
- Add new tests for multi-stage forms

Fixes #2240
2023-02-14 11:51:35 +01:00
JC Brand 5029d93523 XML stanza parsing fixes
- Add a `Stanza` class which can be used by Strophe because it has a
  `tree()` function. This is what gets returned by the `stx` tagged
  template.

- Throw an error when no valid namespace is on the stanza.
    Strophe.Builder used to automatically add the `jabber:client` namespace,
    but that doesn't happen with `toStanza`, so we need to fail if it's not
    specified by the user.

- Use the Strophe XML Parser
    This opens the door to NodeJS support
2023-02-14 11:50:38 +01:00
JC Brand bab11b682b Fix failing test 2023-02-13 20:37:11 +01:00
JC Brand 9343488864 Update translations 2023-02-08 12:06:46 +01:00
Emmanuel Gil Peyrot 782c8c97fa Translated using Weblate (French)
Currently translated at 99.2% (531 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/fr/
2023-02-08 11:08:15 +01:00
JC Brand d2f7756313 Translated using Weblate (Afrikaans)
Currently translated at 98.5% (527 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/af/
2023-02-08 11:08:15 +01:00
Quentin PAGÈS 07371b0852 Translated using Weblate (Occitan)
Currently translated at 75.7% (405 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/oc/
2023-02-08 11:08:15 +01:00
nautilusx 25aa0303c6 Translated using Weblate (German)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/de/
2023-02-08 11:08:15 +01:00
52871299hzy 6186f05a3e Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-02-08 11:08:15 +01:00
jiangshanghan dd4f1e6b85 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-02-08 11:08:15 +01:00
kapad 366390935f Translated using Weblate (Greek)
Currently translated at 58.6% (314 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/el/
2023-02-08 11:08:15 +01:00
josé m 7eb07fa02f Translated using Weblate (Galician)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/gl/
2023-02-08 11:08:15 +01:00
K.Y fbe2e41de7 Translated using Weblate (Uyghur)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/ug/
2023-02-08 11:08:15 +01:00
licat cfc2d93b9b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-02-08 11:08:15 +01:00
Eric 12170461cd Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/zh_Hans/
2023-02-08 11:08:15 +01:00
gallegonovato ab079036da Translated using Weblate (Spanish)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/es/
2023-02-08 11:08:15 +01:00
nautilusx 8001cf3809 Translated using Weblate (German)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/de/
2023-02-08 11:08:15 +01:00
josé m d93a47d246 Translated using Weblate (Galician)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/gl/
2023-02-08 11:08:15 +01:00
Jaime Marquínez Ferrándiz 76fb90319b Translated using Weblate (Spanish)
Currently translated at 98.5% (527 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/es/
2023-02-08 11:08:15 +01:00
nautilusx 9ca1114670 Translated using Weblate (German)
Currently translated at 99.6% (533 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/de/
2023-02-08 11:08:15 +01:00
Oğuz Ersen 251784c21c Translated using Weblate (Turkish)
Currently translated at 100.0% (535 of 535 strings)

Translation: Converse.js/Translations
Translate-URL: https://hosted.weblate.org/projects/conversejs/translations/tr/
2023-02-08 11:08:15 +01:00
JC Brand 8c3ce2892b Updates #3128: Fix test and add changelog entry 2023-01-28 22:50:48 +01:00
JC Brand 6c44fb2a2f Fixes #3128 2023-01-28 22:37:52 +01:00
JC Brand 465aa38222 Refactor bookmark modal
- Use `getDisplayName` to have proper fallback for the MUC name
- Don't use the term "Unbookmark". There might be other reasons for
opening the bookmark modal besides removing a bookmark.
2023-01-28 22:37:52 +01:00
JC Brand 6fc83db9df Run `npm update` 2023-01-17 12:41:48 +01:00
JC Brand a486fe25b8 Configure Webpack to make `@converse/headless` a library
Also, remove unused alias.
2023-01-17 12:33:08 +01:00
Licaon_Kter 87c172acd0 Update info on saved scram 2023-01-10 19:16:30 +01:00
Licaon_Kter 968ec8182c Fix typo 2023-01-10 19:15:39 +01:00
JC Brand 451ed18261 Postrelease and fix issue with Uyghur translations not loading 2023-01-07 21:50:13 +01:00
308 changed files with 76106 additions and 57687 deletions

View File

@ -1,7 +1,7 @@
{
"parser": "@babel/eslint-parser",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2017,
"ecmaVersion": 2020,
"sourceType": "module",
"allowImportExportEverywhere": true
},
@ -10,8 +10,12 @@
"jasmine": true,
"es6": true
},
"plugins": [],
"extends": ["eslint:recommended"],
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"globals": {
"Uint8Array": true,
"Promise": true,

View File

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

3
.gitignore vendored
View File

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

2
.nvmrc
View File

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

View File

@ -1,5 +1,4 @@
{
"arrowParens": "avoid",
"printWidth": 120,
"quoteProps": "preserve",
"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,49 @@
# Changelog
## 10.1.6 (2023-08-31)
- #3246: Badge color not responsive to dark theme
- Fix a GIF rendering bug that causes a memory overflow
## 10.1.5 (2023-06-29)
- #3209: Fix error when importing the `converse` global with bootstrap modal API
- #3207: `.po` translation files weren't included in previous release
- Updated Galician and Portuguese translations
## 10.1.4 (2023-06-25)
- Fix `dist` directory not included in NPM package
## 10.1.3 (2023-06-23)
- Add the ability to set roles and affiliations via the MUC occupant modal
- Fix `isOnlyEmojis is not a function` when using only `@converse/headless`
- Fix `autojoin` checkbox state in MUC bookmark form
- Remove call to `api.confirm` in `@converse/headless`
- Generate TypeScript declaration files into `dist/types`
- Removed documentation about the no longer implemented `fullname` option.
- Updated translations
- #3123: Contacts do not show up online until chat is opened with them.
- #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode
- #3165: Use configured nickname in profile view in the control box
- New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#stanza-timeout)
## 10.1.2 (2023-02-17)
- #1490: Busy-loop when fetching registration form fails
- #1556: Can't switch to registration form afrer logout
- #3137: Various UI/UX bugfixes regarding the registration form
- XEP-0437: Room Activity Indicators (RAI) optimizations
## 10.1.1 (2023-02-15)
- #1851: Sort open groupchats alphabetically
- #2240: Ad-Hoc command result form not shown
- #3128: Second bookmarked room shows info of the first one
- Bugfix. Uyghur translations weren't loading
## 10.1.0 (2023-01-07)
- #326: Add the ability to reset your password
@ -8,9 +52,9 @@
- #2925: File upload is not always enabled
- #3001: Add option to save SCRAM details and to use them to stay logged in upon reload
- Add a "Add to Contacts" button in MUC occupant modals
- Updated tranlsations and new language Uyghur
- Updated translations and add support for Uyghur
- New config option [save_scram_keys](https://conversejs.org/docs/html/configuration.html#save-scram-keys)
- New config option [reuse_scram_keys](https://conversejs.org/docs/html/configuration.html#reuse-scram-keys)
## 10.0.0 (2022-10-30)

View File

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

View File

@ -3,13 +3,11 @@ BOOTSTRAP = ./node_modules/
BUILDDIR = ./docs
KARMA ?= ./node_modules/.bin/karma
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
ESLINT ?= ./node_modules/.bin/eslint
HTTPSERVE ?= ./node_modules/.bin/http-server
HTTPSERVE_PORT ?= 8000
INKSCAPE ?= inkscape
INSTALL ?= install
JSDOC ?= ./node_modules/.bin/jsdoc
LERNA ?= ./node_modules/.bin/lerna
OXIPNG ?= oxipng
PAPER =
RJS ?= ./node_modules/.bin/r.js
@ -60,13 +58,14 @@ serve: node_modules dist
serve_bg: node_modules
$(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
dist/converse-no-dependencies.js: src webpack/webpack.common.js webpack/webpack.nodeps.js @converse/headless node_modules
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.0 dist/converse-no-dependencies.js -c
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
src/i18n/converse.pot: dist/converse-no-dependencies.js
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
@ -83,10 +82,9 @@ po:
########################################################################
## Release management
.PHONY: release
release:
find ./src -name "*~" -exec rm {} \;
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/core.js
.PHONY: version
version:
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/shared/constants.js
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
@ -101,19 +99,33 @@ release:
make pot
make po
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
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
$(LERNA):
npm install lerna
${NVM_DIR}/nvm.sh:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
source ~/.bashrc
@ -125,12 +137,9 @@ nvm: ${NVM_DIR}/nvm.sh
node: .nvmrc
. $(HOME)/.nvm/nvm.sh && nvm install
package-lock.json: package.json
node_modules: package.json src/headless/package.json
npm install
node_modules: $(LERNA) package.json package-lock.json src/headless/package.json src/headless/package-lock.json
npm run lerna
.PHONY: clean
clean:
npm run clean
@ -147,6 +156,9 @@ devserver: node_modules
########################################################################
## 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
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
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
install:: dist
@ -202,20 +223,24 @@ install:: dist
cdn:: node_modules
npm run cdn
.PHONY: types
types:: node_modules
npm run types
########################################################################
## Tests
.PHONY: eslint
eslint: node_modules
$(ESLINT) src/**/*.js
npm run lint
.PHONY: check
check: eslint | dist/converse.js dist/converse.css
$(KARMA) start karma.conf.js $(ARGS)
npm run test -- $(ARGS)
.PHONY: test
test:
$(KARMA) start karma.conf.js $(ARGS)
npm run test -- $(ARGS)
########################################################################
## 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

@ -166,23 +166,23 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
## Sponsors
<p>
<a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="BairesDev" src="https://raw.githubusercontent.com/conversejs/media/main/logos/bairesdev-primary.png" width="200">
</a>
</p>
<p>
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="Blokt Crypto & Privacy" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/blokt.png" width="200">
</a>
</p>
<p>
<a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="Codefirst" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/codefirst.png" width="200">
</a>
</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>
<p>
<a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
<img alt="Prime Sound" src="https://raw.githubusercontent.com/conversejs/media/main/logos/primesound.png" width="200">
</a>
</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
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.
3. Run `make release VERSION=10.1.0`
3. Run `make version VERSION=10.1.6`
4. Do a `git diff` to check if things look sane.
5. Do a quick manual test with the `dist` files (via `index.html`)
6. `git commit -am "Release 10.1.0"`
7. `git tag -s v10.1.0 -m "Release 10.1.0"`
8. Run `git push && git push origin v10.1.0`
9. Update https://conversejs.org
* `cd /home/conversejs/converse.js`
* `git clone --branch v10.1.0 git@github.com:conversejs/converse.js.git 10.1.0`
* `cd 10.1.0 && nvm install && ASSET_PATH=https://cdn.conversejs.org/10.1.0/dist/ make dist && make doc`
* `cd .. && git pull && nvm install && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc`
6. `git commit -am "Release 10.1.6"`
7. `git tag -s v10.1.6 -m "Release 10.1.6"`
8. `git push && git push origin v10.1.6`
9. `make publish BRANCH=v10.1.6`
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
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

@ -1,23 +0,0 @@
# How to use saved Chrome/Chromium logs to replay events
**NOTE**: This feature is very experimental and in many cases doesn't work
without data massaging and ugly hacks.
It's possible to save the log output from Chrome/Chromium (I haven't tried this
yet with any other browser) and then to replay that log output in the browser.
This can be a very helpful technique to track down bugs.
To do this, follow the following steps:
1. Save the log file (right click and then click "Save as" in the browser's console).
2. Rename the log file, making sure it ends in `.html`
3. Move the log file to the `converse-logs` directory in the converse.js repo.
4. Add `<log>` to the top of the log file and `</log>` to the bottom of the log file.
5. In `converse-logs/converse-logs.js`, add a new entry for the log file (don't
include the `.html` part of the file name.
6. Make sure that `spec/transcripts` is "required"-ed in `tests/main.js`
6. Open `tests.html` in your browser.
Your logs will run first, and then all the other tests will run afterwards.

View File

@ -1,5 +0,0 @@
define("transcripts", [
"tpl!../../converse-logs/missing_messages",
], function () {
return arguments;
});

View File

@ -10,6 +10,9 @@
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
<programming-language>JavaScript</programming-language>
<os>Browser</os>
<os>Linux</os>
<os>macOS</os>
<os>Windows</os>
<schema:logo rdf:resource="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/conversejs-filled.svg"/>
<repository>
<GitRepository>

View File

@ -28,6 +28,7 @@
});
converse.initialize({
i18n: 'af',
theme: 'dracula',
auto_away: 300,
enable_smacks: true,
@ -39,10 +40,10 @@
muc_show_logs_before_join: true,
notify_all_room_messages: ['discuss@conference.conversejs.org'],
view_mode: 'fullscreen',
websocket_url: 'wss://conversejs.org/xmpp-websocket',
// websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
// websocket_url: 'wss://conversejs.org/xmpp-websocket',
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
whitelisted_plugins: ['converse-debug'],
connection_options: { worker: '/dist/shared-connection-worker.js' }
// connection_options: { worker: '/dist/shared-connection-worker.js' }
});
</script>
</body>

View File

@ -2,12 +2,8 @@
<h4 class="sidebar-title">Sponsored by</h4>
</span>
<ul class="sponsors-list">
<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>
<li><a href="https://www.codefirst.co.uk/?utm_source=conversejs" target="_blank" rel="noopener">
<img style="width: 9em" src="/logo/codefirst.png" alt="Codefirst">
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
<img style="width: 10em" src="/media/logos/bairesdev-primary.png" alt="BairesDev">
</a>
</li>
<li><a href="https://blokt.com/?utm_source=conversejs" target="_blank" rel="noopener">
@ -18,5 +14,9 @@
<img style="width: 9em" 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: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
</a>
</li>
</ul>
<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.
#
# The short X.Y version.
version = '10.1.0'
version = '10.1.6'
# The full version, including alpha/beta/rc tags.
release = '10.1.0'
release = '10.1.6'
# The language for content autogenerated by Sphinx. Refer to documentation
# 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.
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
------------
@ -2089,6 +2083,13 @@ themselves).
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``.
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
-----------------

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:
* https://cdn.conversejs.org/10.1.0/dist/converse.min.js
* https://cdn.conversejs.org/10.1.0/dist/converse.min.css
* https://cdn.conversejs.org/10.1.6/dist/converse.min.js
* https://cdn.conversejs.org/10.1.6/dist/converse.min.css
You can include these two URLs inside the *<head>* element of your website
via the *script* and *link* tags:
.. code-block:: html
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/10.1.0/dist/converse.min.css">
<script src="https://cdn.conversejs.org/10.1.0/dist/converse.min.js" charset="utf-8"></script>
<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.6/dist/converse.min.js" charset="utf-8"></script>
Option 2: Download the builds from Github

View File

@ -202,5 +202,4 @@ Storing the SASL SCRAM-SHA1 hash in IndexedDB
Another suggestion that's been suggested is to store the SCRAM-SHA1 computed
``clientKey`` in localStorage and to use that upon page reload to log the user in again.
We might implement this feature in core Converse.js eventually.
As always, contributions welcome!
This has been implemented since version 10, see documentation on `reuse_scram_keys <https://conversejs.org/docs/html/configuration.html#reuse-scram-keys>`_

View File

@ -240,11 +240,11 @@
<div class="sponsors">
<h2>Converse is supported by:</h2>
<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://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 13em" src="/media/logos/bairesdev-primary.png" alt="BairesDev"></a></li>
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
</ul>
</div>

View File

@ -11,17 +11,17 @@
<!-- 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 type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.0/css/font-awesome.min.css" />
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.0/css/website.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.6/css/website.min.css" />
<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="analytics.js"></script>
<!-- *********************************************************************** -->
<![if gte IE 11]>
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.0/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/10.1.0/dist/converse.min.js"></script>
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
<![endif]>
</head>
@ -66,7 +66,7 @@
<table id="jslicense-labels1" style="width: 100%">
<tr>
<td>
<a href="https://cdn.conversejs.org/10.1.0/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>
<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/omemo.js", type: 'module' },
{ pattern: "src/plugins/profile/tests/password-reset.js", type: 'module' },
{ pattern: "src/plugins/profile/tests/profile.js", type: 'module' },
{ pattern: "src/plugins/profile/tests/status.js", type: 'module' },
{ pattern: "src/plugins/push/tests/push.js", type: 'module' },
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,19 +1,34 @@
<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>
<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;"
xml:space="preserve" version="1.1"
viewBox="0 0 376 311" height="20%" width="10rem"
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
<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"/>
<metadata id="metadata114">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
<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>
</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>
</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,108 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
class="converse-svg-logo"
viewBox="0 0 364 364"
version="1.1"
id="svg13"
sodipodi:docname="conversejs-with-byline.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<metadata
id="metadata19">
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="chapril-logo"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
class="converse-svg-logo"
xml:space="preserve" version="1.1"
viewBox="0 0 376 311" height="20%" width="6rem"
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
<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"/>
<metadata id="metadata114">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Converse</dc:title>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<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>
</rdf:RDF>
</metadata>
<defs
id="defs17">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 182 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="364 : 182 : 1"
inkscape:persp3d-origin="182 : 121.33333 : 1"
id="perspective2147" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
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>
<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>
<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>

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

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

View File

@ -19,9 +19,9 @@
<script type="text/javascript" src="analytics.js"></script>
<!-- *********************************************************************** -->
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.0/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/10.1.0/dist/converse.min.js"></script>
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
</head>
<body id="page-top" data-spy="scroll" class="converse-website">
@ -241,11 +241,9 @@
<h2>Converse is supported by:</h2>
<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://wikisuite.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 4em" src="/logo/wikisuite-white.png" alt="WikiSuite"></a></li>
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
<li><a href="https://www.b1-systems.de?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 5em" src="/logo/b1-systems.svg" alt="B1 Systems"></a></li>
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
</ul>
</div>

24654
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,46 @@
{
"name": "converse.js",
"version": "10.1.0",
"version": "10.1.6",
"description": "Browser based XMPP chat client",
"browser": "dist/converse.js",
"module": "src/converse.js",
"module": "src/index.js",
"workspaces": [
"src/headless"
],
"files": [
"*.js",
"*.json",
"CHANGES.md",
"LICENSE.txt",
"README.md",
"COPYRIGHT",
"dist/",
"docs/**/*.rst",
"docs/**/*.md",
"docs/**/*.rst",
"sass/**/*.scss",
"src/*.js",
"src/**/*.pot",
"src/**/*.po",
"src/**/*.js",
"src/**/*.html",
"src/**/*.svg",
"src/**/*.js",
"src/**/*.json",
"src/**/*.md",
"src/**/*.po",
"src/**/*.pot",
"src/**/*.scss",
"src/**/*.svg",
"src/**/*.txt",
"src/**/*.json"
"3rdparty/*.js"
],
"scripts": {
"build": "webpack --config webpack/webpack.build.js",
"lint": "eslint src/**/*.js",
"test": "karma start karma.conf",
"cdn": "ASSET_PATH=https://cdn.conversejs.org/dist/ npm run build",
"clean": "rm -rf node_modules dist *.zip src/headless/dist src/headless/node_modules",
"dev": "webpack --config webpack/webpack.build.js --mode=development",
"headless": "webpack --config webpack/webpack.headless.js",
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
"lerna": "lerna bootstrap --hoist --ignore-scripts",
"nodeps": "webpack --config webpack/webpack.nodeps.js",
"prepare": "npm run lerna && npm run build",
"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": {
"type": "git",
@ -68,13 +69,13 @@
"devDependencies": {
"@babel/cli": "^7.17.10",
"@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.18.9",
"@babel/preset-env": "^7.18.2",
"@converse/headless": "file:src/headless",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"autoprefixer": "^10.4.5",
"babel-loader": "^9.1.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",
"css-loader": "^6.7.1",
"eslint": "^8.21.0",
@ -90,41 +91,43 @@
"karma-jasmine": "^5.0.0",
"karma-jasmine-html-reporter": "^2.0.0",
"karma-webpack": "^5.0.0",
"lerna": "^6.0.1",
"mini-css-extract-plugin": "^2.6.0",
"minimist": "^1.2.6",
"po-loader": "0.7.0",
"po2json": "^1.0.0-beta-3",
"postcss": "^8.4.14",
"postcss-clean": "1.2.0",
"postcss": "^8.4.16",
"postcss-loader": "^7.0.1",
"prettierx": "^0.19.0",
"sass": "^1.51.0",
"sass-loader": "^13.1.0",
"style-loader": "^3.1.0",
"webpack": "^5.72.0",
"webpack-cli": "^4.7.2",
"tsc": "^2.0.4",
"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-merge": "^5.8.0"
},
"dependencies": {
"@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.7",
"@converse/skeletor": "^0.0.8",
"bootstrap": "^4.6.0",
"bootstrap.native": "^2.0.27",
"client-compress": "^2.2.2",
"dayjs": "1.11.6",
"dayjs": "^1.11.8",
"dompurify": "^2.3.1",
"favico.js-slevomat": "^0.3.11",
"filesize": "^7.0.0",
"gifuct-js": "^2.1.2",
"jed": "1.1.1",
"lit": "^2.4.0",
"localforage-webextensionstorage-driver": "^3.0.0",
"lodash-es": "^4.17.21",
"pluggable.js": "3.0.1",
"pluggable.js": "^3.0.1",
"sizzle": "^2.3.5",
"sprintf-js": "^1.1.2",
"strophe.js": "1.6.0",
"strophe.js": "^1.6.2",
"urijs": "^1.19.10"
},
"resolutions": {

View File

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

View File

@ -49,7 +49,7 @@ const converse = {
*
* @memberOf converse
* @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
* converse.load({assets_path: '/path/to/assets/'});
*/
@ -57,7 +57,7 @@ const converse = {
if (settings.assets_path) {
__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]));
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 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.

View File

@ -2,681 +2,16 @@
* @copyright The Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
import URI from 'urijs';
import './shared/constants.js';
import _converse from './shared/_converse';
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 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 { i18n };
import {
attemptNonPreboundSession,
cleanup,
initClientConfig,
initConnection,
initPlugins,
initSessionStorage,
registerGlobalEventHandlers,
setUserJID,
} from './utils/init.js';
export { api };
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.0";
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?.nodeTree) {
stanza = stanza.nodeTree;
}
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) {
let promise;
stanza = stanza?.nodeTree ?? stanza;
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
timeout = timeout || _converse.STANZA_TIMEOUT;
if (reject) {
promise = new Promise((resolve, reject) => _converse.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 => _converse.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

@ -2,13 +2,13 @@
* --------------------
* Any of the following components may be removed if they're not needed.
*/
import "./plugins/adhoc.js"; // XEP-0050 Ad Hoc Commands
import "./plugins/bookmarks/index.js"; // XEP-0199 XMPP Ping
import "./plugins/bosh.js"; // XEP-0206 BOSH
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
import "./plugins/chatboxes/index.js";
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
import "./plugins/adhoc/index.js"; // XEP-0050 Ad Hoc Commands
import "./plugins/headlines/index.js"; // Support for headline messages
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat

View File

@ -1,4 +1,4 @@
import isElement from 'lodash-es/isElement';
import { isElement } from './utils/core.js';
const LEVELS = {
'debug': 0,
@ -8,24 +8,26 @@ const LEVELS = {
'fatal': 4
}
/* eslint-disable @typescript-eslint/no-empty-function */
const logger = Object.assign({
'debug': 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 () {},
'warn': console?.log ? console.log.bind(console) : function noop () {}
}, console);
/* eslint-enable @typescript-eslint/no-empty-function */
/**
* The log namespace
* @namespace log
*/
const log = {
export default {
/**
* The the log-level, which determines how verbose the logging is.
* @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) {
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
@ -42,7 +44,7 @@ const log = {
* logged as well.
* @method log#log
* @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='') {
if (LEVELS[level] < LEVELS[this.loglevel]) {
@ -93,5 +95,3 @@ const log = {
this.log(message, 'fatal', style);
}
}
export default log;

View File

@ -1,17 +1,17 @@
{
"name": "@converse/headless",
"version": "10.0.0",
"version": "10.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@converse/headless",
"version": "10.0.0",
"version": "10.1.0",
"license": "MPL-2.0",
"dependencies": {
"@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.7",
"dayjs": "1.11.6",
"@converse/skeletor": "0.0.8",
"dayjs": "1.11.8",
"dompurify": "^2.3.1",
"filesize": "^7.0.0",
"localforage-webextensionstorage-driver": "^3.0.0",
@ -23,29 +23,37 @@
"urijs": "^1.19.10"
}
},
"node_modules/@converse/localforage-getitems": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
"dependencies": {
"localforage": ">=1.4.0"
}
},
"node_modules/@converse/openpromise": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
},
"node_modules/@converse/skeletor": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
"dependencies": {
"@converse/localforage-getitems": "1.4.3",
"lit-html": "^2.0.0-rc.2",
"localforage": "^1.10.0",
"localforage-driver-memory": "^1.0.5",
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
"localforage-setitems": "^1.4.0",
"lodash-es": "^4.17.21",
"mergebounce": "0.1.1"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.3",
@ -145,9 +153,9 @@
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"node_modules/dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
},
"node_modules/filesize": {
"version": "7.0.0",
@ -259,9 +267,9 @@
}
},
"node_modules/lit-html": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.5.0.tgz",
"integrity": "sha512-bLHosg1XL3JRUcKdSVI0sLCs0y1wWrj2sqqAN3cZ7bDDPNgmDHH29RV48x6Wz3ZmkxIupaE+z7uXSZ/pXWAO1g==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
@ -294,14 +302,6 @@
"localforage": "^1.5.0"
}
},
"node_modules/localforage-getitems": {
"version": "1.4.2",
"resolved": "git+ssh://git@github.com/conversejs/localForage-getItems.git#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
"integrity": "sha512-J1Q2IqJgwWNJOOX8Q0UULrBEXZExmLwuGWXp/bsjWpuA5LL8RDJcmbUX/3CD9DqjvxvNTnutw2/973CD/EqXMQ==",
"dependencies": {
"localforage": ">=1.4.0"
}
},
"node_modules/localforage-setitems": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
@ -379,9 +379,9 @@
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"node_modules/rollup": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.9.1.tgz",
"integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==",
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
@ -395,9 +395,9 @@
}
},
"node_modules/sizzle": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.9.tgz",
"integrity": "sha512-mEwbToRr5fME4lcrzHjAs+zHqDhgn9d9URAWi2rvGguv0HUcgpqCKm3DPUkBaViFsi/LvypIQRbmQc+yP3J1ag=="
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
},
"node_modules/sprintf-js": {
"version": "1.1.2",
@ -439,9 +439,9 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"node_modules/ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"optional": true,
"engines": {
"node": ">=10.0.0"
@ -461,29 +461,37 @@
}
},
"dependencies": {
"@converse/localforage-getitems": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
"requires": {
"localforage": ">=1.4.0"
}
},
"@converse/openpromise": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
},
"@converse/skeletor": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
"requires": {
"@converse/localforage-getitems": "1.4.3",
"lit-html": "^2.0.0-rc.2",
"localforage": "^1.10.0",
"localforage-driver-memory": "^1.0.5",
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
"localforage-setitems": "^1.4.0",
"lodash-es": "^4.17.21",
"mergebounce": "0.1.1"
}
},
"@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
},
"@xmldom/xmldom": {
"version": "0.8.3",
@ -558,9 +566,9 @@
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"dompurify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
},
"filesize": {
"version": "7.0.0",
@ -638,9 +646,9 @@
}
},
"lit-html": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.5.0.tgz",
"integrity": "sha512-bLHosg1XL3JRUcKdSVI0sLCs0y1wWrj2sqqAN3cZ7bDDPNgmDHH29RV48x6Wz3ZmkxIupaE+z7uXSZ/pXWAO1g==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
"requires": {
"@types/trusted-types": "^2.0.2"
}
@ -668,14 +676,6 @@
"tslib": "^1.6.0"
}
},
"localforage-getitems": {
"version": "git+ssh://git@github.com/conversejs/localForage-getItems.git#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
"integrity": "sha512-J1Q2IqJgwWNJOOX8Q0UULrBEXZExmLwuGWXp/bsjWpuA5LL8RDJcmbUX/3CD9DqjvxvNTnutw2/973CD/EqXMQ==",
"from": "localforage-getitems@github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
"requires": {
"localforage": ">=1.4.0"
}
},
"localforage-setitems": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
@ -738,18 +738,18 @@
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"rollup": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.9.1.tgz",
"integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==",
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
"peer": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"sizzle": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.9.tgz",
"integrity": "sha512-mEwbToRr5fME4lcrzHjAs+zHqDhgn9d9URAWi2rvGguv0HUcgpqCKm3DPUkBaViFsi/LvypIQRbmQc+yP3J1ag=="
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
},
"sprintf-js": {
"version": "1.1.2",
@ -786,9 +786,9 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"optional": true,
"requires": {}
}

View File

@ -1,12 +1,15 @@
{
"name": "@converse/headless",
"version": "10.1.0",
"version": "10.1.6",
"description": "Converse.js Headless build",
"author": "cmrd Senya <senya@riseup.net>",
"author": "JC Brand <jc@opkode.com>",
"contributors": [
"cmrd Senya <senya@riseup.net>"
],
"homepage": "https://conversejs.org",
"license": "MPL-2.0",
"main": "dist/converse-headless.min.js",
"module": "headless.js",
"main": "dist/converse-headless.js",
"module": "index.js",
"keywords": [
"converse.js",
"XMPP",
@ -27,19 +30,19 @@
"bugs": {
"url": "https://github.com/conversejs/converse.js/issues"
},
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
"dependencies": {
"@converse/openpromise": "^0.0.1",
"@converse/skeletor": "0.0.7",
"dayjs": "1.11.6",
"@converse/skeletor": "^0.0.8",
"dayjs": "^1.11.8",
"dompurify": "^2.3.1",
"filesize": "^7.0.0",
"filesize": "^10.0.7",
"localforage-webextensionstorage-driver": "^3.0.0",
"lodash-es": "^4.17.21",
"pluggable.js": "3.0.1",
"sizzle": "^2.3.5",
"sprintf-js": "^1.1.2",
"strophe.js": "1.6.0",
"strophe.js": "^1.6.2",
"urijs": "^1.19.10"
}
},
"devDependencies": {}
}

View File

@ -1,61 +0,0 @@
import { converse } from "../core.js";
import log from "@converse/headless/log";
import sizzle from 'sizzle';
import { getAttributes } from '@converse/headless/shared/parsers';
const { Strophe } = converse.env;
let _converse, api;
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
function parseForCommands (stanza) {
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
return items.map(getAttributes)
}
const adhoc_api = {
/**
* The XEP-0050 Ad-Hoc Commands API
*
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
*
* @namespace api.adhoc
* @memberOf api
*/
adhoc: {
/**
* @method api.adhoc.getCommands
* @param { String } to_jid
*/
async getCommands (to_jid) {
let commands = [];
try {
commands = parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
} catch (e) {
if (e === null) {
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
} else {
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
log.error(e);
}
}
return commands;
}
}
}
converse.plugins.add('converse-adhoc', {
dependencies: ["converse-disco"],
initialize () {
_converse = this._converse;
api = _converse.api;
Object.assign(api, adhoc_api);
}
});
export default adhoc_api;

View File

@ -0,0 +1,109 @@
import log from '@converse/headless/log';
import { _converse, api, converse } from "@converse/headless/core";
import { getCommandFields, parseForCommands } from './utils.js';
const { Strophe, $iq, u, stx } = converse.env;
export default {
/**
* The XEP-0050 Ad-Hoc Commands API
*
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
*
* @namespace api.adhoc
* @memberOf api
*/
adhoc: {
/**
* @method api.adhoc.getCommands
* @param { String } to_jid
*/
async getCommands (to_jid) {
try {
return parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
} catch (e) {
if (e === null) {
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
} else {
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
log.error(e);
}
return [];
}
},
/**
* @method api.adhoc.fetchCommandForm
*/
async fetchCommandForm (command) {
const node = command.node;
const jid = command.jid;
const stanza = $iq({
'type': 'set',
'to': jid
}).c('command', {
'xmlns': Strophe.NS.ADHOC,
'node': node,
'action': 'execute'
});
try {
return getCommandFields(await api.sendIQ(stanza), jid);
} catch (e) {
if (e === null) {
log.error(`Error: timeout while trying to execute command for ${jid}`);
} else {
log.error(`Error while trying to execute command for ${jid}`);
log.error(e);
}
const { __ } = _converse;
return {
instructions: __('An error occurred while trying to fetch the command form'),
fields: []
}
}
},
/**
* @method api.adhoc.runCommand
* @param { String } jid
* @param { String } sessionid
* @param { 'execute' | 'cancel' | 'prev' | 'next' | 'complete' } action
* @param { String } node
* @param { Array<{ string: string }> } inputs
*/
async runCommand (jid, sessionid, node, action, inputs) {
const iq =
stx`<iq type="set" to="${jid}" xmlns="jabber:client">
<command sessionid="${sessionid}" node="${node}" action="${action}" xmlns="${Strophe.NS.ADHOC}">
${ !['cancel', 'prev'].includes(action) ? stx`
<x xmlns="${Strophe.NS.XFORM}" type="submit">
${ inputs.reduce((out, { name, value }) => out + `<field var="${name}"><value>${value}</value></field>`, '') }
</x>` : '' }
</command>
</iq>`;
const result = await api.sendIQ(iq, null, false);
if (result === null) {
log.warn(`A timeout occurred while trying to run an ad-hoc command`);
const { __ } = _converse;
return {
status: 'error',
note: __('A timeout occurred'),
}
} else if (u.isErrorStanza(result)) {
log.error('Error while trying to execute an ad-hoc command');
log.error(result);
}
const command = result.querySelector('command');
const status = command?.getAttribute('status');
return {
status,
...(status === 'executing' ? getCommandFields(result) : {}),
note: result.querySelector('note')?.textContent
}
}
}
}

View File

@ -0,0 +1,16 @@
import adhoc_api from './api.js';
import { converse } from "@converse/headless/core";
const { Strophe } = converse.env;
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
converse.plugins.add('converse-adhoc', {
dependencies: ["converse-disco"],
initialize () {
Object.assign(this._converse.api, adhoc_api);
}
});

View File

@ -0,0 +1,22 @@
import sizzle from 'sizzle';
import { converse } from "@converse/headless/core";
import { getAttributes } from '@converse/headless/shared/parsers';
const { Strophe, u } = converse.env;
export function parseForCommands (stanza) {
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
return items.map(getAttributes)
}
export function getCommandFields (iq, jid) {
const cmd_el = sizzle(`command[xmlns="${Strophe.NS.ADHOC}"]`, iq).pop();
const data = {
sessionid: cmd_el.getAttribute('sessionid'),
instructions: sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()?.textContent,
fields: sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el)
.map(f => u.xForm2TemplateResult(f, cmd_el, { domain: jid })),
actions: Array.from(cmd_el.querySelector('actions')?.children).map((a) => a.nodeName.toLowerCase()) ?? []
}
return data;
}

View File

@ -136,7 +136,7 @@ converse.plugins.add('converse-bosh', {
tokens: {
/**
* @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.
* @example _converse.api.tokens.get('rid');
*/

View File

@ -31,13 +31,13 @@ async function createCapsNode () {
'hash': "sha-1",
'node': "https://conversejs.org",
'ver': await generateVerificationString()
}).nodeTree;
}).tree();
}
/**
* Given a stanza, adds a XEP-0115 CAPS element
* @param { XMLElement } stanza
* @param { Element } stanza
*/
export async function addCapsNode (stanza) {
const caps_el = await createCapsNode();

View File

@ -13,7 +13,7 @@ export default {
/**
* @method api.chats.create
* @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) {
if (typeof jids === 'string') {
@ -44,9 +44,9 @@ export default {
*
* @method api.chats.open
* @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 {Boolean} [attrs.minimized] - Should the chat be created in minimized state.
* @param {Boolean} [force=false] - By default, a minimized
* @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 } [force=false] - By default, a minimized
* chat won't be maximized (in `overlayed` view mode) and in
* `fullscreen` view mode a newly opened chat won't replace
* another chat already in the foreground.
@ -102,8 +102,8 @@ export default {
*
* @method api.chats.get
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
* @returns { Promise<_converse.ChatBox> }
*
* @example

View File

@ -1,12 +1,13 @@
import ModelWithContact from './model-with-contact.js';
import filesize from "filesize";
import isMatch from "lodash-es/isMatch";
import isObject from "lodash-es/isObject";
import log from '@converse/headless/log';
import pick from "lodash-es/pick";
import { Model } from '@converse/skeletor/src/model.js';
import { TimeoutError } from '../../shared/errors.js';
import { _converse, api, converse } from "../../core.js";
import { debouncedPruneHistory, handleCorrection } from '@converse/headless/shared/chat/utils.js';
import { filesize } from "filesize";
import { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
import { getOpenPromise } from '@converse/openpromise';
import { initStorage } from '@converse/headless/utils/storage.js';
@ -368,7 +369,7 @@ const ChatBox = ModelWithContact.extend({
},
async createMessageFromError (error) {
if (error instanceof _converse.TimeoutError) {
if (error instanceof TimeoutError) {
const msg = await this.createMessage({
'type': 'error',
'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.
* @method st#parseMessage
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @param { _converse } _converse
* @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 { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
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;
@ -13,7 +14,7 @@ export function openChat (jid) {
}
export async function onClearSession () {
if (_converse.shouldClearCache()) {
if (shouldClearCache()) {
await Promise.all(
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
);
@ -88,6 +89,8 @@ export function registerMessageHandlers () {
* @param { MessageAttributes } attrs - The message attributes
*/
export async function handleMessageStanza (stanza) {
stanza = stanza.tree?.() ?? stanza;
if (isServerMessage(stanza)) {
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
const from = stanza.getAttribute('from');
@ -111,7 +114,7 @@ export async function handleMessageStanza (stanza) {
* @typedef { Object } MessageData
* An object containing the original message stanza, as well as the
* parsed attributes.
* @property { XMLElement } stanza
* @property { Element } stanza
* @property { MessageAttributes } stanza
* @property { ChatBox } chatbox
*/

View File

@ -16,7 +16,7 @@ const ChatBoxes = Collection.extend({
* @event _converse#chatBoxesFetched
* @type { object }
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
* @property { XMLElement } stanza
* @property { Element } stanza
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
*/

View File

@ -24,8 +24,8 @@ export default {
stream: {
/**
* @method api.disco.stream.getFeature
* @param {String} name The feature name
* @param {String} xmlns The XML namespace
* @param { String } name The feature name
* @param { String } xmlns The XML namespace
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
*/
async getFeature (name, xmlns) {
@ -57,10 +57,10 @@ export default {
* Lets you add new identities for this client (i.e. instance of Converse)
* @method api.disco.own.identities.add
*
* @param {String} category - server, client, gateway, directory, etc.
* @param {String} type - phone, pc, web, etc.
* @param {String} name - "Converse"
* @param {String} lang - en, el, de, etc.
* @param { String } category - server, client, gateway, directory, etc.
* @param { String } type - phone, pc, web, etc.
* @param { String } name - "Converse"
* @param { String } lang - en, el, de, etc.
*
* @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)
* @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");
*/
add (name) {
@ -134,8 +134,8 @@ export default {
* Query for information about an XMPP entity
*
* @method api.disco.info
* @param {string} jid The Jabber ID of the entity to query
* @param {string} [node] A specific node identifier associated with the JID
* @param { string } jid The Jabber ID of the entity to query
* @param { string } [node] A specific node identifier associated with the JID
* @returns {promise} Promise which resolves once we have a result from the server.
*/
info (jid, node) {
@ -155,8 +155,8 @@ export default {
* Query for items associated with an XMPP entity
*
* @method api.disco.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 } jid The Jabber ID of the entity to query for items
* @param { string } [node] A specific node identifier associated with the JID
* @returns {promise} Promise which resolves once we have a result from the server.
*/
items (jid, node) {
@ -184,8 +184,8 @@ export default {
* Get the corresponding `DiscoEntity` instance.
*
* @method api.disco.entities.get
* @param {string} jid The Jabber ID of the entity
* @param {boolean} [create] Whether the entity should be created if it doesn't exist.
* @param { string } jid The Jabber ID of the entity
* @param { boolean } [create] Whether the entity should be created if it doesn't exist.
* @example _converse.api.disco.entities.get(jid);
*/
async get (jid, create=false) {
@ -209,7 +209,7 @@ export default {
* Return any disco items advertised on this entity
*
* @method api.disco.entities.items
* @param {string} jid The Jabber ID of the entity for which we want to fetch items
* @param { string } jid The Jabber ID of the entity for which we want to fetch items
* @example api.disco.entities.items(jid);
*/
items (jid) {
@ -225,12 +225,12 @@ export default {
* `ignore_cache: true` in the options parameter.
*
* @method api.disco.entities.create
* @param {object} data
* @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.name
* @param {object} [options] - Additional options
* @param {boolean} [options.ignore_cache]
* @param { object } data
* @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.name
* @param { object } [options] - Additional options
* @param { boolean } [options.ignore_cache]
* If true, fetch all features from the XMPP server instead of restoring them from cache
* @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
*/
@ -248,11 +248,11 @@ export default {
* Return a given feature of a disco entity
*
* @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`
* attribute of the `<feature>` element. For
* 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
* @returns {promise} A promise which resolves with a list containing
* _converse.Entity instances representing the entity
@ -285,11 +285,11 @@ export default {
* associated items, supports a given feature.
*
* @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`
* attribute of the `<feature>` element. For
* 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
* @returns {Promise} A promise which resolves with a boolean
* @example
@ -319,11 +319,11 @@ export default {
* Used to determine whether an entity supports a given feature.
*
* @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`
* attribute of the `<feature>` element. For
* 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
* @returns {promise} A promise which resolves with `true` or `false`.
* @example
@ -341,7 +341,7 @@ export default {
* Refresh the features, fields and identities associated with a
* disco entity by refetching them from the server
* @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
* @example
* await api.disco.refresh('room@conference.example.org');
@ -379,7 +379,7 @@ export default {
* Return all the features associated with a disco entity
*
* @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
* @example
* 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)
*
* @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
* 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
*
* @method api.disco.getIdentity
* @param {string} The identity category.
* @param { string } The identity category.
* In the XML stanza, this is the `category`
* attribute of the `<identity>` element.
* For example: 'pubsub'
* @param {string} type The identity type.
* @param { string } type The identity type.
* In the XML stanza, this is the `type`
* attribute of the `<identity>` element.
* 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
* whether an identity with a given type is provided by the entity.
* @example

View File

@ -3,6 +3,7 @@
* @copyright 2022, the Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
import './utils.js';
import { Model } from '@converse/skeletor/src/model.js';
import { _converse, api, converse } from "../../core.js";
import { getOpenPromise } from '@converse/openpromise';
@ -85,8 +86,8 @@ converse.plugins.add('converse-emoji', {
async initialize () {
if (!converse.emojis.initialized) {
converse.emojis.initialized = true;
const { default: json } = await import(/*webpackChunkName: "emojis" */ './emoji.json');
converse.emojis.json = json;
const module = await import(/*webpackChunkName: "emojis" */ './emoji.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.list = Object.values(converse.emojis.by_sn);
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;
* @namespace u
* @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 }
*/
function isOnlyEmojis (text) {

View File

@ -15,8 +15,8 @@ export default {
*
* @method api.headlines.get
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
* @returns { Promise<_converse.HeadlinesFeed> }
*/
async get (jids, attrs={}, create=false) {

View File

@ -4,7 +4,7 @@ import { parseMessage } from '@converse/headless/plugins/chat/parsers';
/**
* Handler method for all incoming messages of type "headline".
* @param { XMLElement } stanza
* @param { Element } stanza
*/
export async function onHeadlineMessage (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 sizzle from "sizzle";
import { RSM } from '@converse/headless/shared/rsm';
import { TimeoutError } from '../../shared/errors.js';
import { _converse, api, converse } from "@converse/headless/core";
const { Strophe, $iq, dayjs } = converse.env;
@ -270,7 +271,7 @@ export default {
const { __ } = _converse;
const err_msg = __("Timeout while trying to fetch archived messages.");
log.error(err_msg);
error = new _converse.TimeoutError(err_msg);
error = new TimeoutError(err_msg);
return { messages, error };
} 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
* 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"
* @param { string } [options.after] - The XEP-0359 stanza ID of a message
* 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.
* @param { _converse.ChatBox | _converse.ChatRoom } model
* @param { ChatBox | ChatRoom } model
* @param { MAMOptions } [options]
* @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether
* 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
* @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations
*/
@ -54,9 +54,9 @@ export function getAssignableAffiliations (occupant) {
if (!Array.isArray(disabled)) {
disabled = disabled ? AFFILIATIONS : [];
}
if (occupant.get('affiliation') === 'owner') {
if (occupant?.get('affiliation') === 'owner') {
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));
} else {
return [];

View File

@ -1,7 +1,8 @@
import log from '../../log';
import u from '../../utils/form';
import { Strophe } from 'strophe.js/src/strophe';
import { _converse, api } from '../../core.js';
import { _converse, api, converse } from '../../core.js';
const { u } = converse.env;
export default {
@ -22,7 +23,7 @@ export default {
* @method api.rooms.create
* @param {(string[]|string)} jid|jids The JID or array of
* 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.
*/
create (jids, attrs = {}) {
@ -44,24 +45,24 @@ export default {
* Similar to {@link api.chats.open}, but for groupchats.
*
* @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).
* @param {string} attrs A map containing any extra room attributes.
* @param {string} [attrs.nick] The current user's nickname for the MUC
* @param {boolean} [attrs.auto_configure] A boolean, indicating
* @param { string } attrs A map containing any extra room attributes.
* @param { string } [attrs.nick] The current user's nickname for the MUC
* @param { boolean } [attrs.auto_configure] A boolean, indicating
* whether the room should be configured automatically or not.
* 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
* `roomconfig` values if `auto_configure` is set to `false`.
* 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).
* 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.bring_to_foreground] A boolean indicating whether the room should be
* @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
* brought to the foreground and therefore replace the currently shown chat.
* 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
* `fullscreen` view mode a newly opened room won't replace
* another chat already in the foreground.

View File

@ -1,4 +1,3 @@
import log from '../../log';
import { Strophe } from 'strophe.js/src/strophe';
import { _converse, api } from '../../core.js';
@ -20,6 +19,7 @@ const ChatRoomMessageMixin = {
this.on('change:type', () => this.setOccupant());
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
this.chatbox = this.collection?.chatbox;
this.setTimerForEphemeralMessage();
this.setOccupant();
/**
@ -53,24 +53,20 @@ const ChatRoomMessageMixin = {
return (
['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) &&
this.get(`stanza_id ${this.get('from_muc')}`) &&
this.collection.chatbox.canModerateMessages()
this.chatbox.canModerateMessages()
);
},
checkValidity () {
const result = _converse.Message.prototype.checkValidity.call(this);
!result && this.collection.chatbox.debouncedRejoin();
!result && this.chatbox.debouncedRejoin();
return result;
},
onOccupantRemoved () {
this.stopListening(this.occupant);
delete this.occupant;
const chatbox = this?.collection?.chatbox;
if (!chatbox) {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
this.listenTo(this.chatbox.occupants, 'add', this.onOccupantAdded);
},
onOccupantAdded (occupant) {
@ -81,10 +77,6 @@ const ChatRoomMessageMixin = {
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
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;
if (occupant.get('jid')) {
@ -93,32 +85,40 @@ const ChatRoomMessageMixin = {
this.trigger('occupantAdded');
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 () {
if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
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 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 = chatbox.occupants.create({ nick, occupant_id, 'type': 'unavailable' });
const jid = `${chatbox.get('jid')}/${nick}`;
api.user.presence.send('probe', jid);
this.occupant = this.chatbox.occupants.findOccupant({ nick, occupant_id });
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);
}
}
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,23 +1,24 @@
import debounce from 'lodash-es/debounce';
import invoke from 'lodash-es/invoke';
import isElement from 'lodash-es/isElement';
import log from '../../log';
import p from '../../utils/parse-helpers';
import pick from 'lodash-es/pick';
import sizzle from 'sizzle';
import u from '../../utils/form';
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 { TimeoutError } from '../../shared/errors.js';
import { _converse, api, converse } from '../../core.js';
import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js';
import { handleCorrection } from '../../shared/chat/utils.js';
import { getOpenPromise } from '@converse/openpromise';
import { handleCorrection } from '../../shared/chat/utils.js';
import { initStorage } from '../../utils/storage.js';
import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
import { parseMUCMessage, parseMUCPresence } from './parsers.js';
import { sendMarker } from '../../shared/actions.js';
import { ROOMSTATUS } from './constants.js';
const { u } = converse.env;
const OWNER_COMMANDS = ['owner'];
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
@ -129,6 +130,15 @@ const ChatRoomMixin = {
return this.session.get('connection_status') === ROOMSTATUS.ENTERED;
},
/**
* Checks whether this MUC qualifies for subscribing to XEP-0437 Room Activity Indicators (RAI)
* @method _converse.ChatRoom#isRAICandidate
* @returns { Boolean }
*/
isRAICandidate () {
return this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none';
},
/**
* Checks whether we're still joined and if so, restores the MUC state from cache.
* @private
@ -136,18 +146,23 @@ const ChatRoomMixin = {
* @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`.
*/
async restoreFromCache () {
if (this.isEntered() && (await this.isJoined())) {
// We've restored the room from cache and we're still joined.
await new Promise(r => this.features.fetch({ 'success': r, 'error': r }));
await new Promise(r => this.config.fetch({ 'success': r, 'error': r }));
if (this.isEntered()) {
await this.fetchOccupants().catch(e => log.error(e));
await this.fetchMessages().catch(e => log.error(e));
return true;
} else {
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
this.clearOccupantsCache();
return false;
if (this.isRAICandidate()) {
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
this.enableRAI();
return true;
} else if (await this.isJoined()) {
await new Promise(r => this.config.fetch({ 'success': r, 'error': r }));
await new Promise(r => this.features.fetch({ 'success': r, 'error': r }));
await this.fetchMessages().catch(e => log.error(e));
return true;
}
}
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
this.clearOccupantsCache();
return false;
},
/**
@ -211,7 +226,7 @@ const ChatRoomMixin = {
* *Hook* which allows plugins to update an outgoing MUC join presence stanza
* @event _converse#constructedMUCPresence
* @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);
return stanza;
@ -276,10 +291,7 @@ const ChatRoomMixin = {
const roomstatus = ROOMSTATUS;
const conn_status = this.session.get('connection_status');
if (this.get('hidden')) {
if (conn_status === roomstatus.ENTERED &&
api.settings.get('muc_subscribe_to_rai') &&
this.getOwnAffiliation() !== 'none') {
if (conn_status === roomstatus.ENTERED && this.isRAICandidate()) {
this.sendMarkerForLastMessage('received', true);
await this.leave();
this.enableRAI();
@ -347,7 +359,7 @@ const ChatRoomMixin = {
async onConnectionStatusChanged () {
if (this.isEntered()) {
if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
if (this.isRAICandidate()) {
try {
await this.leave();
} catch (e) {
@ -495,7 +507,7 @@ const ChatRoomMixin = {
* Handles incoming message stanzas from the service that hosts this MUC
* @private
* @method _converse.ChatRoom#handleMessageFromMUCHost
* @param { XMLElement } stanza
* @param { Element } stanza
*/
handleMessageFromMUCHost (stanza) {
if (this.isEntered()) {
@ -516,7 +528,7 @@ const ChatRoomMixin = {
* Handles XEP-0452 MUC Mention Notification messages
* @private
* @method _converse.ChatRoom#handleForwardedMentions
* @param { XMLElement } stanza
* @param { Element } stanza
*/
handleForwardedMentions (stanza) {
if (this.isEntered()) {
@ -546,9 +558,11 @@ const ChatRoomMixin = {
* Parses an incoming message stanza and queues it for processing.
* @private
* @method _converse.ChatRoom#handleMessageStanza
* @param { XMLElement } stanza
* @param { Element } stanza
*/
async handleMessageStanza (stanza) {
stanza = stanza.tree?.() ?? stanza;
const type = stanza.getAttribute('type');
if (type === 'error') {
return this.handleErrorMessageStanza(stanza);
@ -695,9 +709,9 @@ const ChatRoomMixin = {
* or error message within a specific timeout period.
* @private
* @method _converse.ChatRoom#sendTimedMessage
* @param { _converse.Message|XMLElement } message
* @returns { Promise<XMLElement>|Promise<_converse.TimeoutError> } Returns a promise
* which resolves with the reflected message stanza or with an error stanza or {@link _converse.TimeoutError}.
* @param { _converse.Message|Element } message
* @returns { Promise<Element>|Promise<TimeoutError> } Returns a promise
* which resolves with the reflected message stanza or with an error stanza or {@link TimeoutError}.
*/
sendTimedMessage (el) {
if (typeof el.tree === 'function') {
@ -710,9 +724,10 @@ const ChatRoomMixin = {
el.setAttribute('id', id);
}
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);
const err = new _converse.TimeoutError('Timeout Error: No response from server');
const err = new TimeoutError('Timeout Error: No response from server');
promise.resolve(err);
return false;
});
@ -755,14 +770,14 @@ const ChatRoomMixin = {
message.set({
'retracted': new Date().toISOString(),
'retracted_id': origin_id,
'retraction_id': stanza.nodeTree.getAttribute('id'),
'retraction_id': stanza.tree().getAttribute('id'),
'editable': false
});
const result = await this.sendTimedMessage(stanza);
if (u.isErrorStanza(result)) {
log.error(result);
} else if (result instanceof _converse.TimeoutError) {
} else if (result instanceof TimeoutError) {
log.error(result);
message.save({
editable,
@ -779,7 +794,7 @@ const ChatRoomMixin = {
* Retract someone else's message in this groupchat.
* @private
* @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.
* @example
* const room = await api.rooms.get(jid);
@ -814,7 +829,7 @@ const ChatRoomMixin = {
* Sends an IQ stanza to the XMPP server to retract a message in this groupchat.
* @private
* @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.
*/
sendRetractionIQ (message, reason) {
@ -1229,7 +1244,7 @@ const ChatRoomMixin = {
* 'roomconfig' data.
* @private
* @method _converse.ChatRoom#autoConfigureChatRoom
* @returns { Promise<XMLElement> }
* @returns { Promise<Element> }
* Returns a promise which resolves once a response IQ has
* been received.
*/
@ -1248,7 +1263,7 @@ const ChatRoomMixin = {
* has been received.
* @private
* @method _converse.ChatRoom#fetchRoomConfiguration
* @returns { Promise<XMLElement> }
* @returns { Promise<Element> }
*/
fetchRoomConfiguration () {
return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER }));
@ -1259,7 +1274,7 @@ const ChatRoomMixin = {
* @private
* @method _converse.ChatRoom#sendConfiguration
* @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.
*/
sendConfiguration (config = []) {
@ -1701,7 +1716,7 @@ const ChatRoomMixin = {
* Given a presence stanza, update the occupant model based on its contents.
* @private
* @method _converse.ChatRoom#updateOccupantsOnPresence
* @param { XMLElement } pres - The presence stanza
* @param { Element } pres - The presence stanza
*/
updateOccupantsOnPresence (pres) {
const data = parseMUCPresence(pres, this);
@ -1888,7 +1903,7 @@ const ChatRoomMixin = {
* the `from` attribute. Doesn't check the `type` attribute.
* @private
* @method _converse.ChatRoom#isOwnMessage
* @param { Object|XMLElement|_converse.Message } msg
* @param { Object|Element|_converse.Message } msg
* @returns { boolean }
*/
isOwnMessage (msg) {
@ -1928,6 +1943,10 @@ const ChatRoomMixin = {
* @returns {Promise<boolean>}
*/
async isJoined () {
if (!this.isEntered()) {
log.info(`isJoined: not pinging MUC ${this.get('jid')} since we're not entered`);
return false;
}
if (!api.connection.connected()) {
await new Promise(resolve => api.listen.once('reconnected', resolve));
}
@ -1952,10 +1971,14 @@ const ChatRoomMixin = {
/**
* Check whether we're still joined and re-join if not
* @async
* @method _converse.ChatRoom#rejoinIfNecessary
*/
async rejoinIfNecessary () {
if (this.isRAICandidate()) {
log.debug(`rejoinIfNecessary: not rejoining hidden MUC "${this.get('jid')}" since we're using RAI`);
return true;
}
if (!(await this.isJoined())) {
this.rejoin();
return true;
@ -2128,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
*/
removeNotification (actor, states) {
@ -2152,8 +2175,8 @@ const ChatRoomMixin = {
*
* The state can be a XEP-0085 Chat State or a XEP-0045 join/leave
* state.
* @param {String} actor - The nickname of the actor that causes the notification
* @param {String} state - The state representing the type of notificcation
* @param { String } actor - The nickname of the actor that causes the notification
* @param { String } state - The state representing the type of notificcation
*/
updateNotifications (actor, state) {
const actors_per_state = this.notifications.toJSON();
@ -2201,7 +2224,7 @@ const ChatRoomMixin = {
/**
* Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info
* messages for them.
* @param { XMLElement } stanza
* @param { Element } stanza
*/
handleMEPNotification (attrs) {
if (attrs.from !== this.get('jid') || !attrs.activities) {
@ -2298,7 +2321,7 @@ const ChatRoomMixin = {
/**
* Handle a presence stanza that disconnects the user from the MUC
* @param { XMLElement } stanza
* @param { Element } stanza
*/
handleDisconnection (stanza) {
const is_self = stanza.querySelector("status[code='110']") !== null;
@ -2320,7 +2343,7 @@ const ChatRoomMixin = {
// each <x/> element pertains to a single user.
const item = x.querySelector('item');
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 status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
this.setDisconnectionState(message, reason, actor, status);
@ -2433,7 +2456,7 @@ const ChatRoomMixin = {
* @private
* @method _converse.ChatRoom#createInfoMessage
* @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
*/
createInfoMessage (code, stanza, is_self) {
@ -2476,7 +2499,7 @@ const ChatRoomMixin = {
* Create info messages based on a received presence or message stanza
* @private
* @method _converse.ChatRoom#createInfoMessages
* @param { XMLElement } stanza
* @param { Element } stanza
*/
createInfoMessages (stanza) {
const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
@ -2495,7 +2518,7 @@ const ChatRoomMixin = {
* implied by) the server.
* @param { String } reason - The reason provided for the 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) {
this.session.save({
@ -2532,7 +2555,7 @@ const ChatRoomMixin = {
* `connection_status` value for this {@link _converse.ChatRoom} as
* well as any additional output that can be shown to the user.
* @private
* @param { XMLElement } stanza - The presence stanza
* @param { Element } stanza - The presence stanza
*/
onErrorPresence (stanza) {
const __ = _converse.__;
@ -2597,7 +2620,7 @@ const ChatRoomMixin = {
* Listens for incoming presence stanzas from the service that hosts this MUC
* @private
* @method _converse.ChatRoom#onPresenceFromMUCHost
* @param { XMLElement } stanza - The presence stanza
* @param { Element } stanza - The presence stanza
*/
onPresenceFromMUCHost (stanza) {
if (stanza.getAttribute('type') === 'error') {
@ -2616,7 +2639,7 @@ const ChatRoomMixin = {
* Handles incoming presence stanzas coming from the MUC
* @private
* @method _converse.ChatRoom#onPresence
* @param { XMLElement } stanza
* @param { Element } stanza
*/
onPresence (stanza) {
if (stanza.getAttribute('type') === 'error') {
@ -2649,7 +2672,7 @@ const ChatRoomMixin = {
* user is the groupchat's owner.
* @private
* @method _converse.ChatRoom#onOwnPresence
* @param { XMLElement } pres - The stanza
* @param { Element } pres - The stanza
*/
async onOwnPresence (stanza) {
await this.occupants.fetched;

View File

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

View File

@ -1,14 +1,15 @@
import ChatRoomOccupant from './occupant.js';
import u from '../../utils/form';
import { Collection } from '@converse/skeletor/src/collection.js';
import { MUC_ROLE_WEIGHTS } from './constants.js';
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe.js';
import { _converse, api } from '../../core.js';
import { _converse, api, converse } from '../../core.js';
import { getAffiliationList } from './affiliations/utils.js';
import { getAutoFetchedAffiliationLists } from './utils.js';
import { getUniqueId } from '@converse/headless/utils/core.js';
const { u } = converse.env;
/**
* A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC.

View File

@ -27,7 +27,7 @@ const { NS } = Strophe;
/**
* 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.
*/
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.
*
* @private
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @returns { Object }
*/
function getJIDFromMUCUserData (stanza) {
@ -71,8 +71,8 @@ function getJIDFromMUCUserData (stanza) {
/**
* @private
* @param { XMLElement } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the
* @param { Element } stanza - The message stanza
* @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself.
* @returns { Object }
*/
@ -140,8 +140,8 @@ function getSender (attrs, chatbox) {
/**
* Parses a passed in message stanza and returns an object of attributes.
* @param { XMLElement } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the
* @param { Element } stanza - The message stanza
* @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself.
* @param { _converse.ChatRoom } chatbox
* @param { _converse } _converse
@ -342,7 +342,7 @@ export function parseMemberListIQ (iq) {
/**
* Parses a passed in MUC presence stanza and returns an object of attributes.
* @method parseMUCPresence
* @param { XMLElement } stanza - The presence stanza
* @param { Element } stanza - The presence stanza
* @param { _converse.ChatRoom } chatbox
* @returns { MUCPresenceAttributes }
*/

View File

@ -124,19 +124,19 @@ describe("A MUC message", function () {
const muc_jid = 'lounge@montague.lit';
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const received_stanza = u.toStanza(`
<message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}' >
<reply xmlns='urn:xmpp:reply:0' id='${_converse.connection.getUniqueId()}' to='${_converse.jid}'/>
<fallback xmlns='urn:xmpp:feature-fallback:0' for='urn:xmpp:reply:0'>
<body start='0' end='10'/>
</fallback>
<active xmlns='http://jabber.org/protocol/chatstates'/>
<body>&gt; ping
pong</body>
<request xmlns='urn:xmpp:receipts'/>
</message>
`);
<message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}' >
<reply xmlns='urn:xmpp:reply:0' id='${_converse.connection.getUniqueId()}' to='${_converse.jid}'/>
<fallback xmlns='urn:xmpp:feature-fallback:0' for='urn:xmpp:reply:0'>
<body start='0' end='10'/>
</fallback>
<active xmlns='http://jabber.org/protocol/chatstates'/>
<body>&gt; ping
pong</body>
<request xmlns='urn:xmpp:receipts'/>
</message>
`);
await model.handleMessageStanza(received_stanza);
await u.waitUntil(() => model.messages.last());
expect(model.messages.last().get('body')).toBe('> ping\npong');
expect(model.messages.last().get('body')).toBe('> ping\n pong');
}));
});

View File

@ -83,7 +83,14 @@ describe("A MUC occupant", function () {
await u.waitUntil(() => model.messages.length);
let message = model.messages.at(0);
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');
const presence = u.toStanza(`
@ -98,7 +105,7 @@ describe("A MUC occupant", function () {
</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(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.
* @private
* @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) {
const { __ } = _converse;
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
from = Strophe.getBareJidFromJid(message.getAttribute('from')),
room_jid = x_el.getAttribute('jid'),
@ -99,21 +98,20 @@ export async function onDirectMUCInvitation (message) {
result = true;
} else {
// Invite request might come from someone not your roster list
let contact = _converse.roster.get(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));
} else {
result = await api.confirm(
__(
'%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"',
contact,
room_jid,
reason
)
);
}
const contact = _converse.roster.get(from)?.getDisplayName() ?? from;
/**
* *Hook* which is used to gather confirmation whether a direct MUC
* invitation should be accepted or not.
*
* It's meant for consumers of `@converse/headless` to subscribe to
* this hook and then ask the user to confirm.
*
* @event _converse#confirmDirectMUCInvitation
*/
result = await api.hook('confirmDirectMUCInvitation', { contact, reason, jid: room_jid }, false);
}
if (result) {
const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {

View File

@ -10,7 +10,7 @@ export default {
* @method api.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.
* @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;
* @returns { Boolean | null }
* 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
*
* @method _converse.api.pubsub.publish
* @param {string} jid The JID of the pubsub service where the node resides.
* @param {string} node The node being published to
* @param { string } jid The JID of the pubsub service where the node resides.
* @param { string } node The node being published to
* @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)
* @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
* strict, then Converse will publish to the node even if
* the publish options precondication cannot be met.
@ -74,7 +74,7 @@ converse.plugins.add('converse-pubsub', {
// The publish-options precondition couldn't be
// met. We re-publish but without publish-options.
const el = stanza.nodeTree;
const el = stanza.tree();
el.querySelector('publish-options').outerHTML = '';
log.warn(`PubSub: Republishing without publish options. ${el.outerHTML}`);
await api.sendIQ(el);

View File

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

View File

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

View File

@ -112,7 +112,7 @@ const RosterContacts = Collection.extend({
* @method _converse.RosterContacts#addAndSubscribe
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @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 { 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
* @param { String } jid - The Jabber ID of the user being added
* @param { String } name - The name of that user
* @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
* @param { Array<String> } groups - Any roster groups the user might belong to
*/
sendContactAddIQ (jid, name, groups) {
name = name ? name : null;
@ -148,7 +146,7 @@ const RosterContacts = Collection.extend({
* @method _converse.RosterContacts#addContactToRoster
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @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.
*/
async addContactToRoster (jid, name, groups, attributes) {
@ -190,7 +188,7 @@ const RosterContacts = Collection.extend({
* Handle roster updates from the XMPP server.
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
* @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) {
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).
* @event _converse#rosterPush
* @type { XMLElement }
* @type { Element }
* @example _converse.api.listen.on('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
* `roster` if its already in `sessionStorage`.
* @event _converse#roster
* @type { XMLElement }
* @type { Element }
* @example _converse.api.listen.on('roster', 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
* node received in the resulting IQ stanza from the server.
* @param { XMLElement } item
* @param { Element } item
*/
updateContact (item) {
const jid = item.getAttribute('jid');
@ -379,9 +377,7 @@ const RosterContacts = Collection.extend({
_converse.xmppstatus.save({'status': show}, {'silent': true});
const status_message = presence.querySelector('status')?.textContent;
if (status_message) {
_converse.xmppstatus.save({'status_message': status_message});
}
if (status_message) _converse.xmppstatus.save({ status_message });
}
if (_converse.jid === jid && presence_type === 'unavailable') {
// XXX: We've received an "unavailable" presence from our
@ -414,11 +410,11 @@ const RosterContacts = Collection.extend({
return; // Ignore MUC
}
const status_message = presence.querySelector('status')?.textContent;
const contact = this.get(bare_jid);
if (contact && (status_message !== contact.get('status'))) {
contact.save({'status': status_message});
if (contact) {
const status = presence.querySelector('status')?.textContent;
if (contact.get('status') !== status) contact.save({status});
}
if (presence_type === 'subscribed' && contact) {

View File

@ -30,7 +30,7 @@ export const Presence = Model.extend({
const hpr = this.getHighestPriorityResource();
const show = hpr?.attributes?.show || 'offline';
if (this.get('show') !== show) {
this.save({'show': show});
this.save({ show });
}
},
@ -48,20 +48,20 @@ export const Presence = Model.extend({
* from the passed in presence stanza.
* Also updates the presence if the resource has higher priority (and is newer).
* @private
* @param { XMLElement } presence: The presence stanza
* @param { Element } presence: The presence stanza
*/
addResource (presence) {
const jid = presence.getAttribute('from'),
name = Strophe.getResourceFromJid(jid),
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop(),
priority = presence.querySelector('priority')?.textContent ?? 0,
resource = this.resources.get(name),
settings = {
'name': name,
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
'show': presence.querySelector('show')?.textContent ?? 'online',
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
};
const jid = presence.getAttribute('from');
const name = Strophe.getResourceFromJid(jid);
const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop();
const priority = presence.querySelector('priority')?.textContent;
const resource = this.resources.get(name);
const settings = {
name,
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
'show': presence.querySelector('show')?.textContent ?? 'online',
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
};
if (resource) {
resource.save(settings);
} else {
@ -78,9 +78,7 @@ export const Presence = Model.extend({
*/
removeResource (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 { Model } from '@converse/skeletor/src/model.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 { initStorage } from '@converse/headless/utils/storage.js';
import { shouldClearCache } from '@converse/headless/utils/core.js';
const { $pres } = converse.env;
@ -88,7 +90,7 @@ async function clearPresences () {
*/
export async function onClearSession () {
await clearPresences();
if (_converse.shouldClearCache()) {
if (shouldClearCache()) {
if (_converse.rostergroups) {
await _converse.rostergroups.clearStore();
delete _converse.rostergroups;
@ -197,12 +199,12 @@ export function rejectPresenceSubscription (jid, message) {
export function contactsComparator (contact1, contact2) {
const status1 = contact1.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 name2 = (contact2.getDisplayName()).toLowerCase();
return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
} 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 {
/**
* @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*.
* @namespace _converse.api.user.status
@ -55,15 +25,15 @@ export default {
*
* @async
* @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} [message] A custom status message
* @param { string } value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
* @param { string } [message] A custom status message
*
* @example _converse.api.user.status.set('dnd');
* @example _converse.api.user.status.set('dnd', 'In a meeting');
*/
async set (value, message) {
const data = {'status': value};
if (!Object.keys(_converse.STATUS_WEIGHTS).includes(value)) {
if (!Object.keys(STATUS_WEIGHTS).includes(value)) {
throw new Error(
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
);
@ -85,7 +55,7 @@ export default {
/**
* @async
* @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()
*/
async get () {
@ -95,7 +65,7 @@ export default {
/**
* @async
* @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');
*/
async set (status) {

View File

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

View File

@ -5,10 +5,11 @@ import { _converse, api, converse } from '@converse/headless/core';
const { Strophe, $pres } = converse.env;
const XMPPStatus = Model.extend({
defaults () {
export default class XMPPStatus extends Model {
defaults () { // eslint-disable-line class-methods-use-this
return { "status": api.settings.get("default_state") }
},
}
initialize () {
this.on('change', item => {
@ -19,54 +20,72 @@ const XMPPStatus = Model.extend({
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');
},
}
getFullname () {
// Gets overridden in converse-vcard
return '';
},
getFullname () { // eslint-disable-line class-methods-use-this
return ''; // Gets overridden in converse-vcard
}
/** 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) {
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');
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 === 'error') ||
(type === 'unsubscribe') ||
(type === 'unsubscribed') ||
(type === 'subscribe') ||
(type === 'subscribed')) {
attrs['type'] = type;
presence = $pres(attrs);
presence = $pres({ to, type });
} else if (type === 'offline') {
attrs['type'] = 'unavailable';
presence = $pres(attrs);
presence = $pres({ to, type: 'unavailable' });
} else if (type === 'online') {
presence = $pres(attrs);
presence = $pres({ to });
} else {
presence = $pres(attrs).c('show').t(type).up();
presence = $pres({ to }).c('show').t(type).up();
}
if (status_message) {
presence.c('status').t(status_message).up();
}
if (status_message) presence.c('status').t(status_message).up();
const priority = api.settings.get("priority");
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();
idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
idle_since.setSeconds(idle_since.getSeconds() - idle_seconds);
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);
return presence;
}
});
export default XMPPStatus;
}

View File

@ -22,8 +22,8 @@ export default {
* for the passed in JID.
*
* @method _converse.api.vcard.set
* @param {string} jid The JID for which the VCard should be set
* @param {object} data A map of VCard keys and values
* @param { string } jid The JID for which the VCard should be set
* @param { object } data A map of VCard keys and values
* @example
* let jid = _converse.bare_jid;
* _converse.api.vcard.set( jid, {
@ -67,7 +67,7 @@ export default {
* @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`
* 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.
* @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,
@ -106,8 +106,8 @@ export default {
* returned VCard data.
*
* @method _converse.api.vcard.update
* @param {Model} model A `Model` instance
* @param {boolean} [force] A boolean indicating whether the vcard should be
* @param { Model } model A `Model` instance
* @param { boolean } [force] A boolean indicating whether the vcard should be
* fetched again even if it's been fetched before.
* @returns {promise} A promise which resolves once the update has completed.
* @example

View File

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

View File

@ -1,11 +1,36 @@
import i18n from './i18n.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 { TimeoutError } from './errors.js';
import { createStore, getDefaultStore } from '../utils/storage.js';
import { getInitSettings } from './settings/utils.js';
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 = {
log,
CONNECTION_STATUS,
shouldClearCache, // TODO: Should be moved to utils with next major release
VERSION_NAME,
templates: {},
promises: {
'initialized': getOpenPromise()
},
STATUS_WEIGHTS: {
'offline': 6,
'unavailable': 5,
'xa': 4,
'away': 3,
'dnd': 2,
'chat': 1, // We currently don't differentiate between "chat" and "online"
'online': 1
},
ANONYMOUS: 'anonymous',
CLOSED: 'closed',
EXTERNAL: 'external',
LOGIN: 'login',
LOGOUT: 'logout',
OPENED: 'opened',
PREBIND: 'prebind',
// TODO: remove constants in next major release
ANONYMOUS,
CLOSED,
EXTERNAL,
LOGIN,
LOGOUT,
OPENED,
PREBIND,
/**
* @constant
* @type { integer }
*/
STANZA_TIMEOUT: 20000,
SUCCESS,
FAILURE,
SUCCESS: 'success',
FAILURE: 'failure',
DEFAULT_IMAGE_TYPE,
DEFAULT_IMAGE,
// Generated from css/images/user.svg
DEFAULT_IMAGE_TYPE: 'image/svg+xml',
DEFAULT_IMAGE: "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCI+CiA8cmVjdCB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgZmlsbD0iIzU1NSIvPgogPGNpcmNsZSBjeD0iNjQiIGN5PSI0MSIgcj0iMjQiIGZpbGw9IiNmZmYiLz4KIDxwYXRoIGQ9Im0yOC41IDExMiB2LTEyIGMwLTEyIDEwLTI0IDI0LTI0IGgyMyBjMTQgMCAyNCAxMiAyNCAyNCB2MTIiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==",
INACTIVE,
ACTIVE,
COMPOSING,
PAUSED,
GONE,
PRIVATE_CHAT_TYPE,
CHATROOMS_TYPE,
HEADLINES_TYPE,
CONTROLBOX_TYPE,
// Set as module attr so that we can override in tests.
// TODO: replace with config settings
TIMEOUTS: {
// Set as module attr so that we can override in tests.
PAUSED: 10000,
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},
router: new Router(),
TimeoutError: TimeoutError,
isTestEnv: () => {
return getInitSettings()['bosh_service_url'] === 'montague.lit/http-bind';
},
@ -112,4 +121,10 @@ const _converse = {
'___': str => str
}
// Make _converse an event emitter
Object.assign(_converse, Events);
// Make _converse pluggable
pluggable.enable(_converse, '_converse', 'pluggable');
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,12 +1,12 @@
import debounce from 'lodash-es/debounce';
import isElement from 'lodash-es/isElement';
import log from "../../log.js";
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 { _converse, api } from "../../core.js";
import { getOpenPromise } from '@converse/openpromise';
import { clearSession, tearDown } from "../../utils/core.js";
import { getOpenPromise } from '@converse/openpromise';
import { setUserJID, } from '../../utils/init.js';
const i = Object.keys(Strophe.Status).reduce((max, k) => Math.max(max, Strophe.Status[k]), 0);
@ -128,7 +128,7 @@ export class Connection extends Strophe.Connection {
this._proto = new Strophe.Bosh(this);
this.service = api.settings.get('bosh_service_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
// the domain, not the full JID that we had in our previous
// (now failed) session.
@ -150,7 +150,7 @@ export class Connection extends Strophe.Connection {
const conn_status = _converse.connfeedback.get('connection_status');
if (conn_status === Strophe.Status.CONNFAIL) {
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
// the domain, not the full JID that we had in our previous
// (now failed) session.
@ -164,7 +164,7 @@ export class Connection extends Strophe.Connection {
*/
api.trigger('will-reconnect');
if (api.settings.get("authentication") === _converse.ANONYMOUS) {
if (api.settings.get("authentication") === ANONYMOUS) {
await clearSession();
}
return api.user.login();
@ -265,7 +265,7 @@ export class Connection extends Strophe.Connection {
if (api.settings.get("auto_reconnect")) {
const reason = this.disconnection_reason;
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
// be receiving expirable tokens from the credentials_url.
//
@ -287,7 +287,7 @@ export class Connection extends Strophe.Connection {
);
return this.finishDisconnection();
} else if (
this.disconnection_cause === _converse.LOGOUT ||
this.disconnection_cause === LOGOUT ||
reason === Strophe.ErrorCondition.NO_AUTH_MECH ||
reason === "host-unknown" ||
reason === "remote-connection-failed"
@ -309,7 +309,7 @@ export class Connection extends Strophe.Connection {
*/
onConnectStatusChanged (status, message) {
const { __ } = _converse;
log.debug(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`);
log.debug(`Status changed to: ${CONNECTION_STATUS[status]}`);
if (status === Strophe.Status.ATTACHFAIL) {
this.setConnectionStatus(status);
this.worker_attach_promise?.resolve(false);
@ -441,9 +441,8 @@ export class MockConnection extends Connection {
}
sendIQ (iq, callback, errback) {
if (!isElement(iq)) {
iq = iq.nodeTree;
}
iq = iq.tree?.() ?? iq;
this.IQ_stanzas.push(iq);
const id = super.sendIQ(iq, callback, errback);
this.IQ_ids.push(id);
@ -451,11 +450,8 @@ export class MockConnection extends Connection {
}
send (stanza) {
if (isElement(stanza)) {
this.sent_stanzas.push(stanza);
} else {
this.sent_stanzas.push(stanza.nodeTree);
}
stanza = stanza.tree?.() ?? stanza;
this.sent_stanzas.push(stanza);
return super.send(stanza);
}

View File

@ -1,6 +1,46 @@
import { Strophe } from 'strophe.js/src/strophe';
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 = {};
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.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
// These are just the @converse/headless plugins, for the full converse,
// the other plugins are whitelisted in src/consts.js
@ -35,7 +112,7 @@ export const CORE_PLUGINS = [
'converse-roster',
'converse-smacks',
'converse-status',
'converse-vcard'
'converse-vcard',
];
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,
AT: 50,
META: 91,
META_RIGHT: 93
}
META_RIGHT: 93,
};

View File

@ -1,5 +1,5 @@
/**
* Custom error for indicating timeouts
* @namespace _converse
* @namespace converse.env
*/
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
* and return a map containing them.
* @private
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @returns { Object }
*/
export function getStanzaIDs (stanza, original_stanza) {
@ -72,8 +72,8 @@ export function getEncryptionAttributes (stanza) {
/**
* @private
* @param { XMLElement } stanza - The message stanza
* @param { XMLElement } original_stanza - The original stanza, that contains the
* @param { Element } stanza - The message stanza
* @param { Element } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself.
* @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`.
* @private
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
*/
export function getErrorAttributes (stanza) {
if (stanza.getAttribute('type') === 'error') {
@ -240,7 +240,7 @@ export function getErrorAttributes (stanza) {
/**
* 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 }
*/
export function getReferences (stanza) {
@ -279,7 +279,7 @@ export function getReceiptId (stanza) {
/**
* Determines whether the passed in stanza is a XEP-0280 Carbon
* @private
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @returns { Boolean }
*/
export function isCarbon (stanza) {
@ -293,7 +293,7 @@ export function isCarbon (stanza) {
/**
* Returns the XEP-0085 chat state contained in a message stanza
* @private
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
*/
export function getChatState (stanza) {
return sizzle(
@ -319,7 +319,7 @@ export function isValidReceiptRequest (stanza, attrs) {
/**
* 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.
* @param { XMLElement } stanza
* @param { Element } stanza
*/
export function throwErrorIfInvalidForward (stanza) {
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
* @private
* @method getChatMarker
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @returns { Boolean }
*/
export function getChatMarker (stanza) {
@ -371,7 +371,7 @@ export function isServerMessage (stanza) {
* Determines whether the passed in stanza is a XEP-0313 MAM stanza
* @private
* @method isArchived
* @param { XMLElement } stanza - The message stanza
* @param { Element } stanza - The message stanza
* @returns { Boolean }
*/
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.
* @method getAttributes
* @param { XMLElement } stanza
* @param { Element } stanza
* @returns { Object }
*/
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
* @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 { Integer } [index=0] - The index of the results page to return.
* @property { Integer } [max] - The maximum number of items to return.
* @property { number } [index=0] - The index of the results page to return.
* @property { number } [max] - The maximum number of items to return.
*/
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.
* The element is constructed based on the {@link module:converse-rsm~RSMQueryParameters}
* that are set on this RSM instance.
* @returns { XMLElement }
* @returns { Element }
*/
toXML () {
const xml = $build('set', {xmlns: Strophe.NS.RSM});

View File

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

View File

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

View File

@ -6,21 +6,34 @@
import DOMPurify from 'dompurify';
import _converse from '@converse/headless/shared/_converse.js';
import compact from "lodash-es/compact";
import isElement from "lodash-es/isElement";
import isObject from "lodash-es/isObject";
import last from "lodash-es/last";
import log from '@converse/headless/log.js';
import log from '../log.js';
import sizzle from "sizzle";
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe.js';
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';
/**
* The utils object
* @namespace u
*/
const u = {};
export function isElement (el) {
return el instanceof Element || el instanceof HTMLDocument;
}
export function isError (obj) {
return Object.prototype.toString.call(obj) === "[object Error]";
}
export function isFunction (val) {
return typeof val === 'function';
}
export function isEmptyMessage (attrs) {
if (attrs instanceof Model) {
attrs = attrs.attributes;
@ -32,7 +45,8 @@ export function isEmptyMessage (attrs) {
!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.
* MultiView means that multiple chats may be visible simultaneously.
@ -41,16 +55,24 @@ export function isUniView () {
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 () {
await _converse.api.trigger('beforeTearDown', {'synchronous': true});
const { api } = _converse;
await api.trigger('beforeTearDown', {'synchronous': true});
window.removeEventListener('click', _converse.onUserActivity);
window.removeEventListener('focus', _converse.onUserActivity);
window.removeEventListener('keypress', _converse.onUserActivity);
window.removeEventListener('mousemove', _converse.onUserActivity);
window.removeEventListener(_converse.unloadevent, _converse.onUserActivity);
window.clearInterval(_converse.everySecondTrigger);
_converse.api.trigger('afterTearDown');
api.trigger('afterTearDown');
return _converse;
}
@ -58,7 +80,7 @@ export async function tearDown () {
export function clearSession () {
_converse.session?.destroy();
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,
* for example when the user has logged out or when Converse has
@ -83,16 +105,9 @@ export function prefixMentions (message) {
return text;
}
/**
* The utils object
* @namespace u
*/
const u = {};
u.isTagEqual = function (stanza, name) {
if (stanza.nodeTree) {
return u.isTagEqual(stanza.nodeTree, name);
if (stanza.tree?.()) {
return u.isTagEqual(stanza.tree(), name);
} else if (!(stanza instanceof Element)) {
throw Error(
"isTagEqual called with value which isn't "+
@ -180,7 +195,7 @@ u.isChatRoom = function (model) {
return model && (model.get('type') === 'chatroom');
}
u.isErrorObject = function (o) {
export function isErrorObject (o) {
return o instanceof Error;
}
@ -253,7 +268,7 @@ u.stringToElement = function (s) {
* Checks whether the DOM element matches the given selector.
* @private
* @method u#matchesSelector
* @param { DOMElement } el - The DOM element
* @param { Element } el - The DOM element
* @param { String } selector - The selector
*/
u.matchesSelector = function (el, selector) {
@ -272,7 +287,7 @@ u.matchesSelector = function (el, selector) {
* Returns a list of children of the DOM element that match the selector.
* @private
* @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
*/
u.queryChildren = function (el, selector) {
@ -391,10 +406,10 @@ u.siblingIndex = function (el) {
/**
* Returns the current word being written in the input element
* @method u#getCurrentWord
* @param {HTMLElement} input - The HTMLElement in which text is being entered
* @param {integer} [index] - An optional rightmost boundary index. If given, the text
* @param { HTMLElement } input - The HTMLElement in which text is being entered
* @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.
* @param {string} [delineator] - An optional string delineator to
* @param { string } [delineator] - An optional string delineator to
* differentiate between words.
* @private
*/
@ -474,8 +489,8 @@ export function getUniqueId (suffix) {
/**
* Clears the specified timeout and interval.
* @method u#clearTimers
* @param {number} timeout - Id if the timeout to clear.
* @param {number} interval - Id of the interval to clear.
* @param { number } timeout - Id if the timeout to clear.
* @param { number } interval - Id of the interval to clear.
* @private
* @copyright Simen Bekkhus 2016
* @license MIT
@ -490,16 +505,16 @@ function clearTimers(timeout, interval) {
* 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.
* @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.
* @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 } [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}.
* @returns {Promise} A promise resolved with the value of func,
* or rejected with the exception thrown by it or it times out.
* @copyright Simen Bekkhus 2016
* @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
try {
const result = func();
@ -617,8 +632,12 @@ export function saveWindowState (ev) {
export default Object.assign({
shouldClearCache,
waitUntil, // TODO: remove. Only the API should be used
isErrorObject,
getRandomInt,
getUniqueId,
isElement,
isEmptyMessage,
isValidJID,
merge,

View File

@ -5,13 +5,17 @@
*/
import u from "./core";
const tplXformField = (name, value) => `<field var="${name}">${ value }</field>`;
const tplXformValue = (value) => `<value>${value}</value>`;
/**
* Takes an HTML DOM and turns it into an XForm field.
* @private
* @method u#webForm2xForm
* @param { DOMElement } field - the field to convert
* @param { Element } field - the field to convert
*/
u.webForm2xForm = function (field) {
export function webForm2xForm (field) {
const name = field.getAttribute('name');
if (!name) {
return null; // See #1924
@ -26,11 +30,10 @@ u.webForm2xForm = function (field) {
} else {
value = field.value;
}
return u.toStanza(`
<field var="${name}">
${ value.constructor === Array ?
value.map(v => `<value>${v}</value>`) :
`<value>${value}</value>` }
</field>`);
};
export default u;
return u.toStanza(tplXformField(
name,
Array.isArray(value) ? value.map(tplXformValue) : tplXformValue(value),
));
}
u.webForm2xForm = webForm2xForm;

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