Sfoglia il codice sorgente

Merge branch 'master' into master

JC Brand 6 anni fa
parent
commit
2de794abc1
51 ha cambiato i file con 415 aggiunte e 365 eliminazioni
  1. 1 1
      .github/CONTRIBUTING.rst
  2. 5 2
      CHANGES.md
  3. 1 1
      RELEASE.md
  4. 1 1
      composer.json
  5. 1 1
      css/converse.css
  6. 143 137
      dist/converse.js
  7. 4 4
      docs/source/configuration.rst
  8. 1 1
      docs/source/developer_api.rst
  9. 1 1
      docs/source/index.rst
  10. 5 5
      docs/source/setup.rst
  11. 1 1
      mockup/index.html
  12. 1 1
      sass/converse.scss
  13. 80 39
      spec/autocomplete.js
  14. 3 3
      spec/chatbox.js
  15. 17 20
      spec/muc.js
  16. 1 1
      spec/room_registration.js
  17. 1 1
      spec/roster.js
  18. 15 10
      src/converse-autocomplete.js
  19. 1 1
      src/converse-bookmarks.js
  20. 1 1
      src/converse-caps.js
  21. 1 1
      src/converse-chatboxviews.js
  22. 1 1
      src/converse-chatview.js
  23. 1 1
      src/converse-controlbox.js
  24. 1 1
      src/converse-dragresize.js
  25. 1 1
      src/converse-embedded.js
  26. 1 1
      src/converse-fullscreen.js
  27. 1 1
      src/converse-headline.js
  28. 1 1
      src/converse-minimize.js
  29. 1 1
      src/converse-modal.js
  30. 13 13
      src/converse-muc-views.js
  31. 1 1
      src/converse-notification.js
  32. 1 1
      src/converse-omemo.js
  33. 1 1
      src/converse-profile.js
  34. 1 1
      src/converse-register.js
  35. 1 1
      src/converse-roomslist.js
  36. 1 1
      src/converse-rosterview.js
  37. 1 1
      src/converse-singleton.js
  38. 1 1
      src/headless/converse-chatboxes.js
  39. 1 1
      src/headless/converse-core.js
  40. 2 2
      src/headless/converse-disco.js
  41. 1 1
      src/headless/converse-mam.js
  42. 5 5
      src/headless/converse-muc.js
  43. 1 1
      src/headless/converse-pubsub.js
  44. 1 1
      src/headless/converse-roster.js
  45. 1 1
      src/headless/converse-vcard.js
  46. 1 1
      src/headless/i18n.js
  47. 6 4
      src/headless/utils/core.js
  48. 1 1
      src/headless/utils/form.js
  49. 1 1
      src/headless/utils/muc.js
  50. 79 83
      src/utils/html.js
  51. 1 1
      tests/utils.js

+ 1 - 1
.github/CONTRIBUTING.rst

@@ -2,7 +2,7 @@
 Contribution Guidelines
 Contribution Guidelines
 =======================
 =======================
 
 
-Thanks for contributing to `Converse.js <http://conversejs.org>`_.
+Thanks for contributing to `Converse.js <https://conversejs.org>`_.
 
 
 Support questions
 Support questions
 =================
 =================

+ 5 - 2
CHANGES.md

@@ -2,8 +2,10 @@
 
 
 ## 4.1.3 (Unreleased)
 ## 4.1.3 (Unreleased)
 
 
+- Updated translation: lt
 - Upgrade to Backbone 1.4.0
 - Upgrade to Backbone 1.4.0
 - Fix "flashing" of roster filter when you have less than 5 roster contacts.
 - Fix "flashing" of roster filter when you have less than 5 roster contacts.
+- Fix handling of CAPTCHAs offered by ejabberd.
 - Allow setting of debug mode via URL with `/#converse?debug=true`
 - Allow setting of debug mode via URL with `/#converse?debug=true`
 - New config setting [locked_muc_domain](https://conversejs.org/docs/html/configuration.html#locked-muc-domain)
 - New config setting [locked_muc_domain](https://conversejs.org/docs/html/configuration.html#locked-muc-domain)
 - New config setting [show_client_info](https://conversejs.org/docs/html/configuration.html#show-client-info)
 - New config setting [show_client_info](https://conversejs.org/docs/html/configuration.html#show-client-info)
@@ -13,7 +15,8 @@
 - #1400: When a chat message is just an emoji, enlarge the emoji
 - #1400: When a chat message is just an emoji, enlarge the emoji
 - #1437: List of groupchats in modal doesn't scroll
 - #1437: List of groupchats in modal doesn't scroll
 - #1457: Wrong tooltip shown for "unbookmark" icon
 - #1457: Wrong tooltip shown for "unbookmark" icon
-- #1474: Update Lithuanian (lt) translation
+- #1479: Allow file upload by drag & drop also in MUCs
+
 
 
 ## 4.1.2 (2019-02-22)
 ## 4.1.2 (2019-02-22)
 
 
@@ -647,7 +650,7 @@ More info here: https://github.com/LeaVerou/awesomplete/pull/17082
 ## 2.0.4 (2016-12-13)
 ## 2.0.4 (2016-12-13)
 - #737: Bugfix. Translations weren't being applied. [jcbrand]
 - #737: Bugfix. Translations weren't being applied. [jcbrand]
 - Fetch room info and store it on the room model.
 - Fetch room info and store it on the room model.
-  For context, see: http://xmpp.org/extensions/xep-0045.html#disco-roominfo [jcbrand]
+  For context, see: https://xmpp.org/extensions/xep-0045.html#disco-roominfo [jcbrand]
 - Bugfix. Switching from bookmarks form to config form shows only the spinner. [jcbrand]
 - Bugfix. Switching from bookmarks form to config form shows only the spinner. [jcbrand]
 - Bugfix. Other room occupants sometimes not shown when reloading the page. [jcbrand]
 - Bugfix. Other room occupants sometimes not shown when reloading the page. [jcbrand]
 - Bugfix. Due to changes in `converse-core` the controlbox wasn't aware anymore of
 - Bugfix. Due to changes in `converse-core` the controlbox wasn't aware anymore of

+ 1 - 1
RELEASE.md

@@ -8,7 +8,7 @@
 6. `git commit -am "New release 5.0.0"`
 6. `git commit -am "New release 5.0.0"`
 7. `git tag -s v5.0.0
 7. `git tag -s v5.0.0
 8. Run `git push && git push --tags`
 8. Run `git push && git push --tags`
-9. Update http://conversejs.org
+9. Update https://conversejs.org
 10. Create `5.0.0` directory for the CDN.
 10. Create `5.0.0` directory for the CDN.
     * Create a new version for the CDN by copying
     * Create a new version for the CDN by copying
     * Check out the correct tag
     * Check out the correct tag

+ 1 - 1
composer.json

@@ -4,7 +4,7 @@
     "type": "library",
     "type": "library",
     "license": "MPL-2.0",
     "license": "MPL-2.0",
     "minimum-stability": "stable",
     "minimum-stability": "stable",
-    "homepage": "http://conversejs.org/",
+    "homepage": "https://conversejs.org/",
     "keywords": ["xmpp", "messaging", "chat", "presence"],
     "keywords": ["xmpp", "messaging", "chat", "presence"],
     "require": {}
     "require": {}
 }
 }

+ 1 - 1
css/converse.css

@@ -1,6 +1,6 @@
 /*!
 /*!
  * Converse.js (Web-based XMPP instant messaging client)
  * Converse.js (Web-based XMPP instant messaging client)
- * http://conversejs.org
+ * https://conversejs.org
  *
  *
  * Copyright (c) 2013-2018, JC Brand <jc@opkode.com>
  * Copyright (c) 2013-2018, JC Brand <jc@opkode.com>
  * Licensed under the Mozilla Public License
  * Licensed under the Mozilla Public License

+ 143 - 137
dist/converse.js

@@ -47933,7 +47933,7 @@ exports["filterCSS"] = (filterCSS);
 __webpack_require__.r(__webpack_exports__);
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -48002,14 +48002,15 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
         _.assignIn(this, {
         _.assignIn(this, {
           'match_current_word': false,
           'match_current_word': false,
           // Match only the current word, otherwise all input is matched
           // Match only the current word, otherwise all input is matched
-          'match_on_tab': false,
-          // Whether matching should only start when tab's pressed
-          'trigger_on_at': false,
-          // Whether @ should trigger autocomplete
+          'ac_triggers': [],
+          // Array of keys (`ev.key`) values that will trigger auto-complete
+          'include_triggers': [],
+          // Array of trigger keys which should be included in the returned value
           'min_chars': 2,
           'min_chars': 2,
           'max_items': 10,
           'max_items': 10,
           'auto_evaluate': true,
           'auto_evaluate': true,
           'auto_first': false,
           'auto_first': false,
+          // Should the first element be automatically selected?
           'data': _.identity,
           'data': _.identity,
           'filter': _converse.FILTER_CONTAINS,
           'filter': _converse.FILTER_CONTAINS,
           'sort': config.sort === false ? false : SORT_BYLENGTH,
           'sort': config.sort === false ? false : SORT_BYLENGTH,
@@ -48241,11 +48242,18 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
           return;
           return;
         }
         }
 
 
-        if (this.match_on_tab && ev.keyCode === _converse.keycodes.TAB) {
-          ev.preventDefault();
-          this.auto_completing = true;
-        } else if (this.trigger_on_at && ev.keyCode === _converse.keycodes.AT) {
+        if (this.ac_triggers.includes(ev.key)) {
+          if (ev.key === "Tab") {
+            ev.preventDefault();
+          }
+
           this.auto_completing = true;
           this.auto_completing = true;
+        } else if (ev.key === "Backspace") {
+          const word = u.getCurrentWord(ev.target, ev.target.selectionEnd - 1);
+
+          if (this.ac_triggers.includes(word[0])) {
+            this.auto_completing = true;
+          }
         }
         }
       }
       }
 
 
@@ -48265,7 +48273,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
         let value = this.match_current_word ? u.getCurrentWord(this.input) : this.input.value;
         let value = this.match_current_word ? u.getCurrentWord(this.input) : this.input.value;
         let ignore_min_chars = false;
         let ignore_min_chars = false;
 
 
-        if (this.trigger_on_at && value.startsWith('@')) {
+        if (this.ac_triggers.includes(value[0]) && !this.include_triggers.includes(ev.key)) {
           ignore_min_chars = true;
           ignore_min_chars = true;
           value = value.slice('1');
           value = value.slice('1');
         }
         }
@@ -48391,7 +48399,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chatroom_bookmark_toggle.html */ "./src/templates/chatroom_bookmark_toggle.html");
 /* harmony import */ var templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chatroom_bookmark_toggle.html */ "./src/templates/chatroom_bookmark_toggle.html");
 /* harmony import */ var templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -49043,7 +49051,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
 __webpack_require__.r(__webpack_exports__);
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -49128,7 +49136,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
 /* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
 /* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6__);
 /* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // Copyright (c) 2012-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -49366,7 +49374,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! xss */ "./node_modules/xss/dist/xss.js");
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! xss */ "./node_modules/xss/dist/xss.js");
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_22___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_22__);
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_22___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_22__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -50812,7 +50820,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! templates/login_panel.html */ "./src/templates/login_panel.html");
 /* harmony import */ var templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! templates/login_panel.html */ "./src/templates/login_panel.html");
 /* harmony import */ var templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10__);
 /* harmony import */ var templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -51565,7 +51573,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/dragresize.html */ "./src/templates/dragresize.html");
 /* harmony import */ var templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/dragresize.html */ "./src/templates/dragresize.html");
 /* harmony import */ var templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -51986,7 +51994,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_muc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-muc */ "./src/headless/converse-muc.js");
 /* harmony import */ var _converse_headless_converse_muc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-muc */ "./src/headless/converse-muc.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -52045,7 +52053,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/inverse_brand_heading.html */ "./src/templates/inverse_brand_heading.html");
 /* harmony import */ var templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/inverse_brand_heading.html */ "./src/templates/inverse_brand_heading.html");
 /* harmony import */ var templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) JC Brand <jc@opkode.com>
 // Copyright (c) JC Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -52117,7 +52125,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! templates/chatbox.html */ "./src/templates/chatbox.html");
 /* harmony import */ var templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! templates/chatbox.html */ "./src/templates/chatbox.html");
 /* harmony import */ var templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -52595,7 +52603,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/trimmed_chat.html */ "./src/templates/trimmed_chat.html");
 /* harmony import */ var templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/trimmed_chat.html */ "./src/templates/trimmed_chat.html");
 /* harmony import */ var templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -53212,7 +53220,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/alert_modal.html */ "./src/templates/alert_modal.html");
 /* harmony import */ var templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/alert_modal.html */ "./src/templates/alert_modal.html");
 /* harmony import */ var templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -53392,7 +53400,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! xss */ "./node_modules/xss/dist/xss.js");
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! xss */ "./node_modules/xss/dist/xss.js");
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_26__);
 /* harmony import */ var xss__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_26__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -53528,7 +53536,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
       */
       */
       return str;
       return str;
     }
     }
-    /* http://xmpp.org/extensions/xep-0045.html
+    /* https://xmpp.org/extensions/xep-0045.html
      * ----------------------------------------
      * ----------------------------------------
      * 100 message      Entering a groupchat         Inform user that any occupant is allowed to see the user's full JID
      * 100 message      Entering a groupchat         Inform user that any occupant is allowed to see the user's full JID
      * 101 message (out of band)                     Affiliation change  Inform user that his or her affiliation changed while not in the groupchat
      * 101 message (out of band)                     Affiliation change  Inform user that his or her affiliation changed while not in the groupchat
@@ -53604,7 +53612,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
        *  (XMLElement) stanza: The IQ stanza containing the groupchat
        *  (XMLElement) stanza: The IQ stanza containing the groupchat
        *      info.
        *      info.
        */
        */
-      // All MUC features found here: http://xmpp.org/registrar/disco-features.html
+      // All MUC features found here: https://xmpp.org/registrar/disco-features.html
       el.querySelector('span.spinner').remove();
       el.querySelector('span.spinner').remove();
       el.querySelector('a.room-info').classList.add('selected');
       el.querySelector('a.room-info').classList.add('selected');
       el.insertAdjacentHTML('beforeEnd', templates_room_description_html__WEBPACK_IMPORTED_MODULE_21___default()({
       el.insertAdjacentHTML('beforeEnd', templates_room_description_html__WEBPACK_IMPORTED_MODULE_21___default()({
@@ -53920,7 +53928,9 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         'click .upload-file': 'toggleFileUpload',
         'click .upload-file': 'toggleFileUpload',
         'keydown .chat-textarea': 'keyPressed',
         'keydown .chat-textarea': 'keyPressed',
         'keyup .chat-textarea': 'keyUp',
         'keyup .chat-textarea': 'keyUp',
-        'input .chat-textarea': 'inputChanged'
+        'input .chat-textarea': 'inputChanged',
+        'dragover .chat-textarea': 'onDragOver',
+        'drop .chat-textarea': 'onDrop'
       },
       },
 
 
       initialize() {
       initialize() {
@@ -53976,7 +53986,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         this.renderHeading();
         this.renderHeading();
         this.renderChatArea();
         this.renderChatArea();
         this.renderMessageForm();
         this.renderMessageForm();
-        this.initAutoComplete();
+        this.initMentionAutoComplete();
 
 
         if (this.model.get('connection_status') !== _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED) {
         if (this.model.get('connection_status') !== _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED) {
           this.showSpinner();
           this.showSpinner();
@@ -54006,19 +54016,19 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         return this;
         return this;
       },
       },
 
 
-      initAutoComplete() {
+      initMentionAutoComplete() {
         this.auto_complete = new _converse.AutoComplete(this.el, {
         this.auto_complete = new _converse.AutoComplete(this.el, {
           'auto_first': true,
           'auto_first': true,
           'auto_evaluate': false,
           'auto_evaluate': false,
           'min_chars': 1,
           'min_chars': 1,
           'match_current_word': true,
           'match_current_word': true,
-          'match_on_tab': true,
           'list': () => this.model.occupants.map(o => ({
           'list': () => this.model.occupants.map(o => ({
             'label': o.getDisplayName(),
             'label': o.getDisplayName(),
             'value': `@${o.getDisplayName()}`
             'value': `@${o.getDisplayName()}`
           })),
           })),
           'filter': _converse.FILTER_STARTSWITH,
           'filter': _converse.FILTER_STARTSWITH,
-          'trigger_on_at': true
+          'ac_triggers': ["Tab", "@"],
+          'include_triggers': []
         });
         });
         this.auto_complete.on('suggestion-box-selectcomplete', () => this.auto_completing = false);
         this.auto_complete.on('suggestion-box-selectcomplete', () => this.auto_completing = false);
       },
       },
@@ -54261,7 +54271,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
          * ignore <gone/> notifications in groupchats.
          * ignore <gone/> notifications in groupchats.
          *
          *
          * As laid out in the business rules in XEP-0085
          * As laid out in the business rules in XEP-0085
-         * http://xmpp.org/extensions/xep-0085.html#bizrules-groupchat
+         * https://xmpp.org/extensions/xep-0085.html#bizrules-groupchat
          */
          */
         if (message.get('fullname') === this.model.get('nick')) {
         if (message.get('fullname') === this.model.get('nick')) {
           // Don't know about other servers, but OpenFire sends
           // Don't know about other servers, but OpenFire sends
@@ -55179,7 +55189,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
 
 
       showStatusMessages(stanza) {
       showStatusMessages(stanza) {
         /* Check for status codes and communicate their purpose to the user.
         /* Check for status codes and communicate their purpose to the user.
-         * See: http://xmpp.org/registrar/mucstatus.html
+         * See: https://xmpp.org/registrar/mucstatus.html
          *
          *
          * Parameters:
          * Parameters:
          *  (XMLElement) stanza: The message or presence stanza
          *  (XMLElement) stanza: The message or presence stanza
@@ -55358,9 +55368,6 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         const show = this.model.get('show');
         const show = this.model.get('show');
         return templates_occupant_html__WEBPACK_IMPORTED_MODULE_20___default()(_.extend({
         return templates_occupant_html__WEBPACK_IMPORTED_MODULE_20___default()(_.extend({
           '_': _,
           '_': _,
-          // XXX Normally this should already be included,
-          // but with the current webpack build,
-          // we only get a subset of the _ methods.
           'jid': '',
           'jid': '',
           'show': show,
           'show': show,
           'hint_show': _converse.PRETTY_CHAT_STATUS[show],
           'hint_show': _converse.PRETTY_CHAT_STATUS[show],
@@ -55732,7 +55739,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
 __webpack_require__.r(__webpack_exports__);
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -56061,7 +56068,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! templates/toolbar_omemo.html */ "./src/templates/toolbar_omemo.html");
 /* harmony import */ var templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! templates/toolbar_omemo.html */ "./src/templates/toolbar_omemo.html");
 /* harmony import */ var templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -57495,7 +57502,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! templates/status_option.html */ "./src/templates/status_option.html");
 /* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! templates/status_option.html */ "./src/templates/status_option.html");
 /* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__);
 /* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -57988,7 +57995,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_spinner_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_spinner_html__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var templates_spinner_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_spinner_html__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var _converse_headless_utils_form__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @converse/headless/utils/form */ "./src/headless/utils/form.js");
 /* harmony import */ var _converse_headless_utils_form__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @converse/headless/utils/form */ "./src/headless/utils/form.js");
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -58749,7 +58756,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/rooms_list_item.html */ "./src/templates/rooms_list_item.html");
 /* harmony import */ var templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! templates/rooms_list_item.html */ "./src/templates/rooms_list_item.html");
 /* harmony import */ var templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -59107,7 +59114,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! templates/search_contact.html */ "./src/templates/search_contact.html");
 /* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! templates/search_contact.html */ "./src/templates/search_contact.html");
 /* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__);
 /* harmony import */ var templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -60229,7 +60236,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var converse_chatview__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! converse-chatview */ "./src/converse-chatview.js");
 /* harmony import */ var converse_chatview__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! converse-chatview */ "./src/converse-chatview.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -61577,7 +61584,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var filesize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! filesize */ "./node_modules/filesize/lib/filesize.js");
 /* harmony import */ var filesize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! filesize */ "./node_modules/filesize/lib/filesize.js");
 /* harmony import */ var filesize__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(filesize__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var filesize__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(filesize__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // Copyright (c) 2012-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -63029,7 +63036,7 @@ _converse.TIMEOUTS = {
   'PAUSED': 10000,
   'PAUSED': 10000,
   'INACTIVE': 90000
   'INACTIVE': 90000
 }; // XEP-0085 Chat states
 }; // XEP-0085 Chat states
-// http://xmpp.org/extensions/xep-0085.html
+// https://xmpp.org/extensions/xep-0085.html
 
 
 _converse.INACTIVE = 'inactive';
 _converse.INACTIVE = 'inactive';
 _converse.ACTIVE = 'active';
 _converse.ACTIVE = 'active';
@@ -64798,7 +64805,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse developers
 // Copyright (c) 2013-2019, the Converse developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -65024,7 +65031,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
     });
     });
 
 
     function addClientFeatures() {
     function addClientFeatures() {
-      // See http://xmpp.org/registrar/disco-categories.html
+      // See https://xmpp.org/registrar/disco-categories.html
       _converse.api.disco.own.identities.add('client', 'web', 'Converse');
       _converse.api.disco.own.identities.add('client', 'web', 'Converse');
 
 
       _converse.api.disco.own.features.add(Strophe.NS.BOSH);
       _converse.api.disco.own.features.add(Strophe.NS.BOSH);
@@ -65553,7 +65560,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_3__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -66224,7 +66231,7 @@ function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d =
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
 
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -66834,7 +66841,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
         /* Send an IQ stanza to the server, asking it for the
         /* Send an IQ stanza to the server, asking it for the
          * member-list of this groupchat.
          * member-list of this groupchat.
          *
          *
-         * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+         * See: https://xmpp.org/extensions/xep-0045.html#modifymember
          *
          *
          * Parameters:
          * Parameters:
          *  (String) affiliation: The specific member list to
          *  (String) affiliation: The specific member list to
@@ -66860,7 +66867,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
         /* Send IQ stanzas to the server to set an affiliation for
         /* Send IQ stanzas to the server to set an affiliation for
          * the provided JIDs.
          * the provided JIDs.
          *
          *
-         * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+         * See: https://xmpp.org/extensions/xep-0045.html#modifymember
          *
          *
          * XXX: Prosody doesn't accept multiple JIDs' affiliations
          * XXX: Prosody doesn't accept multiple JIDs' affiliations
          * being set in one IQ stanza, so as a workaround we send
          * being set in one IQ stanza, so as a workaround we send
@@ -67059,7 +67066,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
         /* Send IQ stanzas to the server to modify the
         /* Send IQ stanzas to the server to modify the
          * affiliations in this groupchat.
          * affiliations in this groupchat.
          *
          *
-         * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+         * See: https://xmpp.org/extensions/xep-0045.html#modifymember
          *
          *
          * Parameters:
          * Parameters:
          *  (Object) members: A map of jids, affiliations and optionally reasons
          *  (Object) members: A map of jids, affiliations and optionally reasons
@@ -67912,7 +67919,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
          *     configured automatically. Currently it doesn't make sense to specify
          *     configured automatically. Currently it doesn't make sense to specify
          *     `roomconfig` values if `auto_configure` is set to `false`.
          *     `roomconfig` values if `auto_configure` is set to `false`.
          *     For a list of configuration values that can be passed in, refer to these values
          *     For a list of configuration values that can be passed in, refer to these values
-         *     in the [XEP-0045 MUC specification](http://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
+         *     in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
          *     The values should be named without the `muc#roomconfig_` prefix.
          *     The values should be named without the `muc#roomconfig_` prefix.
          * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
          * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
          *     maximized, when opened. Set to `false` by default.
          *     maximized, when opened. Set to `false` by default.
@@ -68173,7 +68180,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_disco__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./converse-disco */ "./src/headless/converse-disco.js");
 /* harmony import */ var _converse_disco__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./converse-disco */ "./src/headless/converse-disco.js");
 /* harmony import */ var _converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2019, the Converse.js developers
 // Copyright (c) 2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -68296,7 +68303,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins.add('converse-pub
 __webpack_require__.r(__webpack_exports__);
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -69390,7 +69397,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _templates_vcard_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./templates/vcard.html */ "./src/headless/templates/vcard.html");
 /* harmony import */ var _templates_vcard_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./templates/vcard.html */ "./src/headless/templates/vcard.html");
 /* harmony import */ var _templates_vcard_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_templates_vcard_html__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _templates_vcard_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_templates_vcard_html__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -69714,7 +69721,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! moment */ "./node_modules/moment/moment.js");
 /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! moment */ "./node_modules/moment/moment.js");
 /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_30___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_30__);
 /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_30___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_30__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the internationalization module.
 // This is the internationalization module.
 //
 //
@@ -70080,7 +70087,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! sizzle */ "./node_modules/sizzle/dist/sizzle.js");
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var sizzle__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_4__);
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //
@@ -70420,9 +70427,12 @@ u.siblingIndex = function (el) {
   return i;
   return i;
 };
 };
 
 
-u.getCurrentWord = function (input) {
-  const cursor = input.selectionEnd || undefined;
-  return _lodash_noconflict__WEBPACK_IMPORTED_MODULE_3___default.a.last(input.value.slice(0, cursor).split(' '));
+u.getCurrentWord = function (input, index) {
+  if (!index) {
+    index = input.selectionEnd || undefined;
+  }
+
+  return _lodash_noconflict__WEBPACK_IMPORTED_MODULE_3___default.a.last(input.value.slice(0, index).split(' '));
 };
 };
 
 
 u.replaceCurrentWord = function (input, new_value) {
 u.replaceCurrentWord = function (input, new_value) {
@@ -92075,7 +92085,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _templates_field_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_templates_field_html__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _templates_field_html__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_templates_field_html__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //
@@ -92125,7 +92135,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //
@@ -95611,7 +95621,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _templates_video_html__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(_templates_video_html__WEBPACK_IMPORTED_MODULE_15__);
 /* harmony import */ var _templates_video_html__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(_templates_video_html__WEBPACK_IMPORTED_MODULE_15__);
 /* harmony import */ var _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../headless/utils/core */ "./src/headless/utils/core.js");
 /* harmony import */ var _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../headless/utils/core */ "./src/headless/utils/core.js");
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is a form utilities module.
 // This is a form utilities module.
 //
 //
@@ -96168,86 +96178,82 @@ _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].xForm2webForm = fu
    *  Parameters:
    *  Parameters:
    *      (XMLElement) field - the field to convert
    *      (XMLElement) field - the field to convert
    */
    */
-  if (field.getAttribute('type')) {
-    if (field.getAttribute('type') === 'list-single' || field.getAttribute('type') === 'list-multi') {
-      const values = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.map(_headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].queryChildren(field, 'value'), _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.partial(_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get, _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a, 'textContent'));
+  if (field.getAttribute('type') === 'list-single' || field.getAttribute('type') === 'list-multi') {
+    const values = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.map(_headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].queryChildren(field, 'value'), _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.partial(_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get, _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a, 'textContent'));
 
 
-      const options = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.map(_headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].queryChildren(field, 'option'), function (option) {
-        const value = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(option.querySelector('value'), 'textContent');
+    const options = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.map(_headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].queryChildren(field, 'option'), function (option) {
+      const value = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(option.querySelector('value'), 'textContent');
 
 
-        return _templates_select_option_html__WEBPACK_IMPORTED_MODULE_14___default()({
-          'value': value,
-          'label': option.getAttribute('label'),
-          'selected': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.includes(values, value),
-          'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
-        });
-      });
-
-      return _templates_form_select_html__WEBPACK_IMPORTED_MODULE_9___default()({
-        'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
-        'name': field.getAttribute('var'),
-        'label': field.getAttribute('label'),
-        'options': options.join(''),
-        'multiple': field.getAttribute('type') === 'list-multi',
+      return _templates_select_option_html__WEBPACK_IMPORTED_MODULE_14___default()({
+        'value': value,
+        'label': option.getAttribute('label'),
+        'selected': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.includes(values, value),
         'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
         'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
       });
       });
-    } else if (field.getAttribute('type') === 'fixed') {
-      const text = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent');
-
-      return '<p class="form-help">' + text + '</p>';
-    } else if (field.getAttribute('type') === 'jid-multi') {
-      return _templates_form_textarea_html__WEBPACK_IMPORTED_MODULE_10___default()({
-        'name': field.getAttribute('var'),
-        'label': field.getAttribute('label') || '',
-        'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent'),
-        'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
-      });
-    } else if (field.getAttribute('type') === 'boolean') {
-      return _templates_form_checkbox_html__WEBPACK_IMPORTED_MODULE_7___default()({
-        'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
-        'name': field.getAttribute('var'),
-        'label': field.getAttribute('label') || '',
-        'checked': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent') === "1" && 'checked="1"' || '',
-        'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
-      });
-    } else if (field.getAttribute('var') === 'url') {
-      return _templates_form_url_html__WEBPACK_IMPORTED_MODULE_11___default()({
-        'label': field.getAttribute('label') || '',
-        'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent')
-      });
-    } else if (field.getAttribute('var') === 'username') {
-      return _templates_form_username_html__WEBPACK_IMPORTED_MODULE_12___default()({
-        'domain': ' @' + domain,
-        'name': field.getAttribute('var'),
-        'type': XFORM_TYPE_MAP[field.getAttribute('type')],
-        'label': field.getAttribute('label') || '',
-        'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent'),
-        'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
-      });
-    } else {
-      return _templates_form_input_html__WEBPACK_IMPORTED_MODULE_8___default()({
-        'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
-        'label': field.getAttribute('label') || '',
-        'name': field.getAttribute('var'),
-        'placeholder': null,
-        'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required')),
-        'type': XFORM_TYPE_MAP[field.getAttribute('type')],
-        'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent')
-      });
-    }
+    });
+
+    return _templates_form_select_html__WEBPACK_IMPORTED_MODULE_9___default()({
+      'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
+      'name': field.getAttribute('var'),
+      'label': field.getAttribute('label'),
+      'options': options.join(''),
+      'multiple': field.getAttribute('type') === 'list-multi',
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
+    });
+  } else if (field.getAttribute('type') === 'fixed') {
+    const text = _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent');
+
+    return '<p class="form-help">' + text + '</p>';
+  } else if (field.getAttribute('type') === 'jid-multi') {
+    return _templates_form_textarea_html__WEBPACK_IMPORTED_MODULE_10___default()({
+      'name': field.getAttribute('var'),
+      'label': field.getAttribute('label') || '',
+      'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent'),
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
+    });
+  } else if (field.getAttribute('type') === 'boolean') {
+    return _templates_form_checkbox_html__WEBPACK_IMPORTED_MODULE_7___default()({
+      'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
+      'name': field.getAttribute('var'),
+      'label': field.getAttribute('label') || '',
+      'checked': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent') === "1" && 'checked="1"' || '',
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
+    });
+  } else if (field.getAttribute('var') === 'url') {
+    return _templates_form_url_html__WEBPACK_IMPORTED_MODULE_11___default()({
+      'label': field.getAttribute('label') || '',
+      'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent')
+    });
+  } else if (field.getAttribute('var') === 'username') {
+    return _templates_form_username_html__WEBPACK_IMPORTED_MODULE_12___default()({
+      'domain': ' @' + domain,
+      'name': field.getAttribute('var'),
+      'type': XFORM_TYPE_MAP[field.getAttribute('type')],
+      'label': field.getAttribute('label') || '',
+      'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent'),
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
+    });
+  } else if (field.getAttribute('var') === 'ocr') {
+    // Captcha
+    const uri = field.querySelector('uri');
+    const el = sizzle__WEBPACK_IMPORTED_MODULE_2___default()('data[cid="' + uri.textContent.replace(/^cid:/, '') + '"]', stanza)[0];
+    return _templates_form_captcha_html__WEBPACK_IMPORTED_MODULE_6___default()({
+      'label': field.getAttribute('label'),
+      'name': field.getAttribute('var'),
+      'data': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(el, 'textContent'),
+      'type': uri.getAttribute('type'),
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
+    });
   } else {
   } else {
-    if (field.getAttribute('var') === 'ocr') {
-      // Captcha
-      const uri = field.querySelector('uri');
-      const el = sizzle__WEBPACK_IMPORTED_MODULE_2___default()('data[cid="' + uri.textContent.replace(/^cid:/, '') + '"]', stanza)[0];
-      return _templates_form_captcha_html__WEBPACK_IMPORTED_MODULE_6___default()({
-        'label': field.getAttribute('label'),
-        'name': field.getAttribute('var'),
-        'data': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(el, 'textContent'),
-        'type': uri.getAttribute('type'),
-        'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required'))
-      });
-    }
+    return _templates_form_input_html__WEBPACK_IMPORTED_MODULE_8___default()({
+      'id': _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].getUniqueId(),
+      'label': field.getAttribute('label') || '',
+      'name': field.getAttribute('var'),
+      'placeholder': null,
+      'required': !_headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.isNil(field.querySelector('required')),
+      'type': XFORM_TYPE_MAP[field.getAttribute('type')],
+      'value': _headless_lodash_noconflict__WEBPACK_IMPORTED_MODULE_1___default.a.get(field.querySelector('value'), 'textContent')
+    });
   }
   }
 };
 };
 
 

+ 4 - 4
docs/source/configuration.rst

@@ -222,7 +222,7 @@ allow_registration
 
 
 * Default:  ``true``
 * Default:  ``true``
 
 
-Support for `XEP-0077: In band registration <http://xmpp.org/extensions/xep-0077.html>`_
+Support for `XEP-0077: In band registration <https://xmpp.org/extensions/xep-0077.html>`_
 
 
 Allow XMPP account registration showing the corresponding UI register form interface.
 Allow XMPP account registration showing the corresponding UI register form interface.
 
 
@@ -572,7 +572,7 @@ csi_waiting_time
 
 
 * Default: ``0``
 * Default: ``0``
 
 
-This option adds support for `XEP-0352 Client State Indication <http://xmpp.org/extensions/xep-0352.html>_`
+This option adds support for `XEP-0352 Client State Indication <https://xmpp.org/extensions/xep-0352.html>_`
 
 
 If Converse is idle for the configured amount of seconds, a chat state
 If Converse is idle for the configured amount of seconds, a chat state
 indication of ``inactive`` will be sent out to the XMPP server (if the server
 indication of ``inactive`` will be sent out to the XMPP server (if the server
@@ -806,7 +806,7 @@ See also:
     Currently the "keepalive" setting only works with BOSH and not with
     Currently the "keepalive" setting only works with BOSH and not with
     websockets. This is because XMPP over websocket does not use the same
     websockets. This is because XMPP over websocket does not use the same
     session token as with BOSH. A possible solution for this is to implement
     session token as with BOSH. A possible solution for this is to implement
-    `XEP-0198 <http://xmpp.org/extensions/xep-0198.html>`_, specifically
+    `XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`_, specifically
     with regards to "stream resumption".
     with regards to "stream resumption".
 
 
 .. _`locales`:
 .. _`locales`:
@@ -993,7 +993,7 @@ muc_instant_rooms
 
 
 Determines whether 'instant' (also called 'dynamic' in OpenFire) rooms are created.
 Determines whether 'instant' (also called 'dynamic' in OpenFire) rooms are created.
 Otherwise rooms first have to be configured before they're available to other
 Otherwise rooms first have to be configured before they're available to other
-users (so-called "registered rooms" in `MUC-0045 <http://xmpp.org/extensions/xep-0045.html#createroom>`_).
+users (so-called "registered rooms" in `MUC-0045 <https://xmpp.org/extensions/xep-0045.html#createroom>`_).
 
 
 From a UX perspective, if this settings is `false`, then a configuration form will
 From a UX perspective, if this settings is `false`, then a configuration form will
 render, that has to be filled in first, before the room can be joined by other
 render, that has to be filled in first, before the room can be joined by other

+ 1 - 1
docs/source/developer_api.rst

@@ -1050,7 +1050,7 @@ Room attributes that may be passed in:
   configured automatically. Currently it doesn't make sense to specify
   configured automatically. Currently it doesn't make sense to specify
   ``roomconfig`` values if ``auto_configure`` is set to ``false``.
   ``roomconfig`` values if ``auto_configure`` is set to ``false``.
   For a list of configuration values that can be passed in, refer to these values
   For a list of configuration values that can be passed in, refer to these values
-  in the `XEP-0045 MUC specification <http://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner>`_.
+  in the `XEP-0045 MUC specification <https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner>`_.
   The values should be named without the ``muc#roomconfig_`` prefix.
   The values should be named without the ``muc#roomconfig_`` prefix.
 * *maximize*: A boolean, indicating whether minimized rooms should also be
 * *maximize*: A boolean, indicating whether minimized rooms should also be
   maximized, when opened. Set to ``false`` by default.
   maximized, when opened. Set to ``false`` by default.

+ 1 - 1
docs/source/index.rst

@@ -20,7 +20,7 @@ contribute, please read the :doc:`documentation` page.
 Introduction
 Introduction
 ============
 ============
 
 
-Converse is a free and open-source `XMPP <http://xmpp.org/about-xmpp/>`_
+Converse is a free and open-source `XMPP <https://xmpp.org/about-xmpp/>`_
 chat client written in JavaScript which can be tightly integrated into any website.
 chat client written in JavaScript which can be tightly integrated into any website.
 
 
 The benefit of using converse.js as opposed to relying on a SaaS
 The benefit of using converse.js as opposed to relying on a SaaS

+ 5 - 5
docs/source/setup.rst

@@ -57,7 +57,7 @@ The various components
 An XMPP server
 An XMPP server
 ==============
 ==============
 
 
-Converse uses `XMPP <http://xmpp.org/about-xmpp/>`_ as its
+Converse uses `XMPP <https://xmpp.org/about-xmpp/>`_ as its
 messaging protocol, and therefore needs to connect to an XMPP/Jabber
 messaging protocol, and therefore needs to connect to an XMPP/Jabber
 server (Jabber® is an older and more user-friendly synonym for XMPP).
 server (Jabber® is an older and more user-friendly synonym for XMPP).
 
 
@@ -67,7 +67,7 @@ authentication sessions to log in users to the XMPP server (i.e. :ref:`session s
 then you'll have to set up your own XMPP server.
 then you'll have to set up your own XMPP server.
 
 
 You can find a list of public XMPP servers/providers on `compliance.conversations.im <http://compliance.conversations.im/>`_
 You can find a list of public XMPP servers/providers on `compliance.conversations.im <http://compliance.conversations.im/>`_
-and a list of servers that you can set up yourself on `xmpp.org <http://xmpp.org/xmpp-software/servers/>`_.
+and a list of servers that you can set up yourself on `xmpp.org <https://xmpp.org/xmpp-software/servers/>`_.
 
 
 .. _`BOSH-section`:
 .. _`BOSH-section`:
 
 
@@ -90,7 +90,7 @@ server, we need a proxy which acts as a bridge between these two protocols.
 
 
 This is the job of a BOSH connection manager. BOSH (Bidirectional-streams Over
 This is the job of a BOSH connection manager. BOSH (Bidirectional-streams Over
 Synchronous HTTP) is a protocol for allowing XMPP communication over HTTP. The
 Synchronous HTTP) is a protocol for allowing XMPP communication over HTTP. The
-protocol is defined in `XEP-0206: XMPP Over BOSH <http://xmpp.org/extensions/xep-0206.html>`_.
+protocol is defined in `XEP-0206: XMPP Over BOSH <https://xmpp.org/extensions/xep-0206.html>`_.
 
 
 Popular XMPP servers such as `Ejabberd <http://www.ejabberd.im>`_,
 Popular XMPP servers such as `Ejabberd <http://www.ejabberd.im>`_,
 Prosody `(mod_bosh) <http://prosody.im/doc/setting_up_bosh>`_ and
 Prosody `(mod_bosh) <http://prosody.im/doc/setting_up_bosh>`_ and
@@ -104,7 +104,7 @@ https://conversejs.org does), then you'll need a standalone connection manager.
 For a standalone manager, see for example `Punjab <https://github.com/twonds/punjab>`_
 For a standalone manager, see for example `Punjab <https://github.com/twonds/punjab>`_
 and `node-xmpp-bosh <https://github.com/dhruvbird/node-xmpp-bosh>`_.
 and `node-xmpp-bosh <https://github.com/dhruvbird/node-xmpp-bosh>`_.
 
 
-The demo on the `Converse homepage <http://conversejs.org>`_ uses a connection
+The demo on the `Converse homepage <https://conversejs.org>`_ uses a connection
 manager located at https://conversejs.org/http-bind.
 manager located at https://conversejs.org/http-bind.
 
 
 This connection manager is available for testing purposes only, please don't
 This connection manager is available for testing purposes only, please don't
@@ -258,7 +258,7 @@ Option 1). Server-side authentication via BOSH prebinding
 ---------------------------------------------------------
 ---------------------------------------------------------
 
 
 To **prebind** refers to a technique whereby your web application sets up an
 To **prebind** refers to a technique whereby your web application sets up an
-authenticated BOSH session with the XMPP server or a standalone `BOSH <http://xmpp.org/about-xmpp/technology-overview/bosh/>`_
+authenticated BOSH session with the XMPP server or a standalone `BOSH <https://xmpp.org/about-xmpp/technology-overview/bosh/>`_
 connection manager.
 connection manager.
 
 
 Once authenticated, it receives RID and SID tokens which need to be passed
 Once authenticated, it receives RID and SID tokens which need to be passed

+ 1 - 1
mockup/index.html

@@ -11,7 +11,7 @@
 <body>
 <body>
     <div id="header_wrap" class="outer">
     <div id="header_wrap" class="outer">
         <header class="inner">
         <header class="inner">
-          <h1 id="project_title"><a href="http://conversejs.org">Converse.js</a></h1>
+          <h1 id="project_title"><a href="https://conversejs.org">Converse.js</a></h1>
           <h2 id="project_tagline">Mockups</h2>
           <h2 id="project_tagline">Mockups</h2>
         </header>
         </header>
     </div>
     </div>

+ 1 - 1
sass/converse.scss

@@ -1,6 +1,6 @@
 /*!
 /*!
  * Converse.js (Web-based XMPP instant messaging client)
  * Converse.js (Web-based XMPP instant messaging client)
- * http://conversejs.org
+ * https://conversejs.org
  *
  *
  * Copyright (c) 2013-2018, JC Brand <jc@opkode.com>
  * Copyright (c) 2013-2018, JC Brand <jc@opkode.com>
  * Licensed under the Mozilla Public License
  * Licensed under the Mozilla Public License

+ 80 - 39
spec/autocomplete.js

@@ -18,45 +18,44 @@
         it("shows all autocompletion options when the user presses @",
         it("shows all autocompletion options when the user presses @",
             mock.initConverse(
             mock.initConverse(
                 null, ['rosterGroupsFetched'], {},
                 null, ['rosterGroupsFetched'], {},
-                    function (done, _converse) {
-
-            test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom')
-            .then(() => {
-                const view = _converse.chatboxviews.get('lounge@localhost');
-
-                ['dick', 'harry'].forEach((nick) => {
-                    _converse.connection._dataRecv(test_utils.createRequest(
-                        $pres({
-                            'to': 'tom@localhost/resource',
-                            'from': `lounge@localhost/${nick}`
-                        })
-                        .c('x', {xmlns: Strophe.NS.MUC_USER})
-                        .c('item', {
-                            'affiliation': 'none',
-                            'jid': `${nick}@localhost/resource`,
-                            'role': 'participant'
-                        })));
-                });
+                    async function (done, _converse) {
+
+            await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom');
+            const view = _converse.chatboxviews.get('lounge@localhost');
+
+            ['dick', 'harry'].forEach((nick) => {
+                _converse.connection._dataRecv(test_utils.createRequest(
+                    $pres({
+                        'to': 'tom@localhost/resource',
+                        'from': `lounge@localhost/${nick}`
+                    })
+                    .c('x', {xmlns: Strophe.NS.MUC_USER})
+                    .c('item', {
+                        'affiliation': 'none',
+                        'jid': `${nick}@localhost/resource`,
+                        'role': 'participant'
+                    })));
+            });
 
 
-                // Test that pressing @ brings up all options
-                const textarea = view.el.querySelector('textarea.chat-textarea');
-                const at_event = {
-                    'target': textarea,
-                    'preventDefault': _.noop,
-                    'stopPropagation': _.noop,
-                    'keyCode': 50
-                };
-                view.keyPressed(at_event);
-                textarea.value = '@';
-                view.keyUp(at_event);
-
-                expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(3);
-                expect(view.el.querySelector('.suggestion-box__results li[aria-selected="true"]').textContent).toBe('tom');
-                expect(view.el.querySelector('.suggestion-box__results li:first-child').textContent).toBe('tom');
-                expect(view.el.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('dick');
-                expect(view.el.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('harry');
-                done();
-            }).catch(_.partial(console.error, _));
+            // Test that pressing @ brings up all options
+            const textarea = view.el.querySelector('textarea.chat-textarea');
+            const at_event = {
+                'target': textarea,
+                'preventDefault': _.noop,
+                'stopPropagation': _.noop,
+                'keyCode': 50,
+                'key': '@'
+            };
+            view.keyPressed(at_event);
+            textarea.value = '@';
+            view.keyUp(at_event);
+
+            expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(3);
+            expect(view.el.querySelector('.suggestion-box__results li[aria-selected="true"]').textContent).toBe('tom');
+            expect(view.el.querySelector('.suggestion-box__results li:first-child').textContent).toBe('tom');
+            expect(view.el.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('dick');
+            expect(view.el.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('harry');
+            done();
         }));
         }));
 
 
         it("autocompletes when the user presses tab",
         it("autocompletes when the user presses tab",
@@ -88,7 +87,8 @@
                 'target': textarea,
                 'target': textarea,
                 'preventDefault': _.noop,
                 'preventDefault': _.noop,
                 'stopPropagation': _.noop,
                 'stopPropagation': _.noop,
-                'keyCode': 9
+                'keyCode': 9,
+                'key': 'Tab'
             }
             }
             view.keyPressed(tab_event);
             view.keyPressed(tab_event);
             view.keyUp(tab_event);
             view.keyUp(tab_event);
@@ -168,5 +168,46 @@
             expect(textarea.value).toBe('hello @z3r0 ');
             expect(textarea.value).toBe('hello @z3r0 ');
             done();
             done();
         }));
         }));
+
+        it("autocompletes when the user presses backspace",
+            mock.initConverse(
+                null, ['rosterGroupsFetched'], {},
+                    async function (done, _converse) {
+
+            await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
+            const view = _converse.chatboxviews.get('lounge@localhost');
+            expect(view.model.occupants.length).toBe(1);
+            const presence = $pres({
+                    'to': 'dummy@localhost/resource',
+                    'from': 'lounge@localhost/some1'
+                })
+                .c('x', {xmlns: Strophe.NS.MUC_USER})
+                .c('item', {
+                    'affiliation': 'none',
+                    'jid': 'some1@localhost/resource',
+                    'role': 'participant'
+                });
+            _converse.connection._dataRecv(test_utils.createRequest(presence));
+            expect(view.model.occupants.length).toBe(2);
+
+            const textarea = view.el.querySelector('textarea.chat-textarea');
+            textarea.value = "hello @some1 ";
+
+            // Press backspace
+            const backspace_event = {
+                'target': textarea,
+                'preventDefault': _.noop,
+                'stopPropagation': _.noop,
+                'keyCode': 8,
+                'key': 'Backspace'
+            }
+            view.keyPressed(backspace_event);
+            textarea.value = "hello @some1"; // Mimic backspace
+            view.keyUp(backspace_event);
+            expect(view.el.querySelector('.suggestion-box__results').hidden).toBeFalsy();
+            expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(1);
+            expect(view.el.querySelector('.suggestion-box__results li').textContent).toBe('some1');
+            done();
+        }));
     });
     });
 }));
 }));

+ 3 - 3
spec/chatbox.js

@@ -671,7 +671,7 @@
                         await test_utils.waitForRoster(_converse, 'current');
                         await test_utils.waitForRoster(_converse, 'current');
                         test_utils.openControlBox();
                         test_utils.openControlBox();
 
 
-                        // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
+                        // See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit');
                         spyOn(_converse, 'emit');
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
@@ -817,7 +817,7 @@
                         test_utils.openControlBox();
                         test_utils.openControlBox();
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
                         // TODO: only show paused state if the previous state was composing
                         // TODO: only show paused state if the previous state was composing
-                        // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
+                        // See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit').and.callThrough();
                         spyOn(_converse, 'emit').and.callThrough();
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const view = await test_utils.openChatBoxFor(_converse, sender_jid);
                         const view = await test_utils.openChatBoxFor(_converse, sender_jid);
@@ -985,7 +985,7 @@
                         await test_utils.waitForRoster(_converse, 'current');
                         await test_utils.waitForRoster(_converse, 'current');
                         test_utils.openControlBox();
                         test_utils.openControlBox();
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
-                        // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
+                        // See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit');
                         spyOn(_converse, 'emit');
                         await test_utils.openChatBoxFor(_converse, sender_jid);
                         await test_utils.openChatBoxFor(_converse, sender_jid);
                         const view = _converse.chatboxviews.get(sender_jid);
                         const view = _converse.chatboxviews.get(sender_jid);

+ 17 - 20
spec/muc.js

@@ -347,7 +347,7 @@
                 // The user has just entered the room (because join was called)
                 // The user has just entered the room (because join was called)
                 // and receives their own presence from the server.
                 // and receives their own presence from the server.
                 // See example 24:
                 // See example 24:
-                // http://xmpp.org/extensions/xep-0045.html#enter-pres
+                // https://xmpp.org/extensions/xep-0045.html#enter-pres
                 //
                 //
                 /* <presence xmlns="jabber:client" to="jordie.langen@chat.example.org/converse.js-11659299" from="myroom@conference.chat.example.org/jc">
                 /* <presence xmlns="jabber:client" to="jordie.langen@chat.example.org/converse.js-11659299" from="myroom@conference.chat.example.org/jc">
                  *    <x xmlns="http://jabber.org/protocol/muc#user">
                  *    <x xmlns="http://jabber.org/protocol/muc#user">
@@ -1219,7 +1219,7 @@
 
 
                 /* Check that an IQ is sent out, asking for the
                 /* Check that an IQ is sent out, asking for the
                  * configuration form.
                  * configuration form.
-                 * See: // http://xmpp.org/extensions/xep-0045.html#example-163
+                 * See: // https://xmpp.org/extensions/xep-0045.html#example-163
                  *
                  *
                  *  <iq from='crone1@shakespeare.lit/desktop'
                  *  <iq from='crone1@shakespeare.lit/desktop'
                  *      id='config1'
                  *      id='config1'
@@ -1234,7 +1234,7 @@
                     `</iq>`);
                     `</iq>`);
 
 
                 /* Server responds with the configuration form.
                 /* Server responds with the configuration form.
-                 * See: // http://xmpp.org/extensions/xep-0045.html#example-165
+                 * See: // https://xmpp.org/extensions/xep-0045.html#example-165
                  */
                  */
                 var config_stanza = $iq({from: 'coven@chat.shakespeare.lit',
                 var config_stanza = $iq({from: 'coven@chat.shakespeare.lit',
                     'id': IQ_id,
                     'id': IQ_id,
@@ -1398,7 +1398,7 @@
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                     name = mock.chatroom_names[i];
                     name = mock.chatroom_names[i];
                     role = mock.chatroom_roles[name].role;
                     role = mock.chatroom_roles[name].role;
-                    // See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
+                    // See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
                     jid =
                     jid =
                     presence = $pres({
                     presence = $pres({
                             to:'dummy@localhost/pda',
                             to:'dummy@localhost/pda',
@@ -1418,11 +1418,11 @@
                 }
                 }
 
 
                 // Test users leaving the groupchat
                 // Test users leaving the groupchat
-                // http://xmpp.org/extensions/xep-0045.html#exit
+                // https://xmpp.org/extensions/xep-0045.html#exit
                 for (i=mock.chatroom_names.length-1; i>-1; i--) {
                 for (i=mock.chatroom_names.length-1; i>-1; i--) {
                     name = mock.chatroom_names[i];
                     name = mock.chatroom_names[i];
                     role = mock.chatroom_roles[name].role;
                     role = mock.chatroom_roles[name].role;
-                    // See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
+                    // See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
                     presence = $pres({
                     presence = $pres({
                         to:'dummy@localhost/pda',
                         to:'dummy@localhost/pda',
                         from:'lounge@localhost/'+name,
                         from:'lounge@localhost/'+name,
@@ -1452,7 +1452,7 @@
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                     name = mock.chatroom_names[i];
                     name = mock.chatroom_names[i];
                     role = mock.chatroom_roles[name].role;
                     role = mock.chatroom_roles[name].role;
-                    // See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
+                    // See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
                     jid =
                     jid =
                     presence = $pres({
                     presence = $pres({
                             to:'dummy@localhost/pda',
                             to:'dummy@localhost/pda',
@@ -1472,11 +1472,11 @@
                 }
                 }
 
 
                 // Test users leaving the groupchat
                 // Test users leaving the groupchat
-                // http://xmpp.org/extensions/xep-0045.html#exit
+                // https://xmpp.org/extensions/xep-0045.html#exit
                 for (i=mock.chatroom_names.length-1; i>-1; i--) {
                 for (i=mock.chatroom_names.length-1; i>-1; i--) {
                     name = mock.chatroom_names[i];
                     name = mock.chatroom_names[i];
                     role = mock.chatroom_roles[name].role;
                     role = mock.chatroom_roles[name].role;
-                    // See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
+                    // See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres
                     presence = $pres({
                     presence = $pres({
                         to:'dummy@localhost/pda',
                         to:'dummy@localhost/pda',
                         from:'lounge@localhost/'+name,
                         from:'lounge@localhost/'+name,
@@ -1695,7 +1695,7 @@
                 // The user has just entered the groupchat (because join was called)
                 // The user has just entered the groupchat (because join was called)
                 // and receives their own presence from the server.
                 // and receives their own presence from the server.
                 // See example 24:
                 // See example 24:
-                // http://xmpp.org/extensions/xep-0045.html#enter-pres
+                // https://xmpp.org/extensions/xep-0045.html#enter-pres
                 const presence = $pres({
                 const presence = $pres({
                         to:'dummy@localhost/resource',
                         to:'dummy@localhost/resource',
                         from:'lounge@localhost/thirdwitch',
                         from:'lounge@localhost/thirdwitch',
@@ -1946,7 +1946,7 @@
                     async function (done, _converse) {
                     async function (done, _converse) {
 
 
                 await test_utils.openAndEnterChatRoom(_converse, 'jdev', 'conference.jabber.org', 'jc');
                 await test_utils.openAndEnterChatRoom(_converse, 'jdev', 'conference.jabber.org', 'jc');
-                const text = 'Jabber/XMPP Development | RFCs and Extensions: http://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org';
+                const text = 'Jabber/XMPP Development | RFCs and Extensions: https://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org';
                 let stanza = u.toStanza(`
                 let stanza = u.toStanza(`
                     <message xmlns="jabber:client" to="jc@opkode.com/_converse.js-60429116" type="groupchat" from="jdev@conference.jabber.org/ralphm">
                     <message xmlns="jabber:client" to="jc@opkode.com/_converse.js-60429116" type="groupchat" from="jdev@conference.jabber.org/ralphm">
                         <subject>${text}</subject>
                         <subject>${text}</subject>
@@ -2008,7 +2008,7 @@
                  * nickname and one indicating availability for the new
                  * nickname and one indicating availability for the new
                  * nickname.
                  * nickname.
                  *
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#changenick
+                 * See: https://xmpp.org/extensions/xep-0045.html#changenick
                  *
                  *
                  *  <presence
                  *  <presence
                  *      from='coven@localhost/thirdwitch'
                  *      from='coven@localhost/thirdwitch'
@@ -4082,7 +4082,7 @@
                 spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
                 spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
                 roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
                 roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
 
 
-                // See: http://xmpp.org/extensions/xep-0045.html#disco-rooms
+                // See: https://xmpp.org/extensions/xep-0045.html#disco-rooms
                 expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
                 expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
 
 
                 const server_input = modal.el.querySelector('input[name="server"]');
                 const server_input = modal.el.querySelector('input[name="server"]');
@@ -4299,7 +4299,7 @@
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                         .toBe("nomorenicks has entered the groupchat");
                         .toBe("nomorenicks has entered the groupchat");
 
 
-                    // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
+                    // See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
 
 
                     // <composing> state
                     // <composing> state
                     let msg = $msg({
                     let msg = $msg({
@@ -4368,10 +4368,8 @@
                     expect(notifications.length).toBe(2);
                     expect(notifications.length).toBe(2);
                     expect(notifications[0].textContent).toEqual('newguy is typing');
                     expect(notifications[0].textContent).toEqual('newguy is typing');
                     expect(notifications[1].textContent).toEqual('nomorenicks is typing');
                     expect(notifications[1].textContent).toEqual('nomorenicks is typing');
-                    expect(timeout_functions.length).toBe(3);
 
 
-                    // Check that new messages appear under the chat state
-                    // notifications
+                    // Check that new messages appear under the chat state notifications
                     msg = $msg({
                     msg = $msg({
                         from: `${room_jid}/some1`,
                         from: `${room_jid}/some1`,
                         id: (new Date()).getTime(),
                         id: (new Date()).getTime(),
@@ -4386,8 +4384,7 @@
                     expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                     expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                     expect(view.el.querySelector('.chat-msg .chat-msg__text').textContent).toBe('hello world');
                     expect(view.el.querySelector('.chat-msg .chat-msg__text').textContent).toBe('hello world');
 
 
-                    // Test that the composing notifications get removed
-                    // via timeout.
+                    // Test that the composing notifications get removed via timeout.
                     timeout_functions[0]();
                     timeout_functions[0]();
                     events = view.el.querySelectorAll('.chat-event');
                     events = view.el.querySelectorAll('.chat-event');
                     expect(events.length).toBe(3);
                     expect(events.length).toBe(3);
@@ -4475,7 +4472,7 @@
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                         .toBe("nomorenicks has entered the groupchat");
                         .toBe("nomorenicks has entered the groupchat");
 
 
-                    // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
+                    // See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
 
 
                     // <composing> state
                     // <composing> state
                     var msg = $msg({
                     var msg = $msg({

+ 1 - 1
spec/room_registration.js

@@ -112,7 +112,7 @@
                 }).then(stanza => {
                 }).then(stanza => {
                     // The user has just entered the room (because join was called)
                     // The user has just entered the room (because join was called)
                     // and receives their own presence from the server.
                     // and receives their own presence from the server.
-                    // See example 24: http://xmpp.org/extensions/xep-0045.html#enter-pres
+                    // See example 24: https://xmpp.org/extensions/xep-0045.html#enter-pres
                     const presence = $pres({
                     const presence = $pres({
                             to: _converse.connection.jid,
                             to: _converse.connection.jid,
                             from: room_jid,
                             from: room_jid,

+ 1 - 1
spec/roster.js

@@ -1270,7 +1270,7 @@
                     expect(_converse.roster.pluck('jid').length).toBe(1);
                     expect(_converse.roster.pluck('jid').length).toBe(1);
                     expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
                     expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
                     // Taken from the spec
                     // Taken from the spec
-                    // http://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
+                    // https://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
                     stanza = $iq({
                     stanza = $iq({
                         to: _converse.connection.jid,
                         to: _converse.connection.jid,
                         type: 'result',
                         type: 'result',

+ 15 - 10
src/converse-autocomplete.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -13,7 +13,6 @@ import converse from "@converse/headless/converse-core";
 const { _, Backbone } = converse.env,
 const { _, Backbone } = converse.env,
       u = converse.env.utils;
       u = converse.env.utils;
 
 
-
 converse.plugins.add("converse-autocomplete", {
 converse.plugins.add("converse-autocomplete", {
 
 
     initialize () {
     initialize () {
@@ -73,12 +72,12 @@ converse.plugins.add("converse-autocomplete", {
 
 
                 _.assignIn(this, {
                 _.assignIn(this, {
                     'match_current_word': false, // Match only the current word, otherwise all input is matched
                     'match_current_word': false, // Match only the current word, otherwise all input is matched
-                    'match_on_tab': false, // Whether matching should only start when tab's pressed
-                    'trigger_on_at': false, // Whether @ should trigger autocomplete
+                    'ac_triggers': [], // Array of keys (`ev.key`) values that will trigger auto-complete
+                    'include_triggers': [], // Array of trigger keys which should be included in the returned value
                     'min_chars': 2,
                     'min_chars': 2,
                     'max_items': 10,
                     'max_items': 10,
                     'auto_evaluate': true,
                     'auto_evaluate': true,
-                    'auto_first': false,
+                    'auto_first': false, // Should the first element be automatically selected?
                     'data': _.identity,
                     'data': _.identity,
                     'filter': _converse.FILTER_CONTAINS,
                     'filter': _converse.FILTER_CONTAINS,
                     'sort': config.sort === false ? false : SORT_BYLENGTH,
                     'sort': config.sort === false ? false : SORT_BYLENGTH,
@@ -291,11 +290,17 @@ converse.plugins.add("converse-autocomplete", {
                         , ev.keyCode)) {
                         , ev.keyCode)) {
                     return;
                     return;
                 }
                 }
-                if (this.match_on_tab && ev.keyCode === _converse.keycodes.TAB) {
-                    ev.preventDefault();
-                    this.auto_completing = true;
-                } else if (this.trigger_on_at && ev.keyCode === _converse.keycodes.AT) {
+
+                if (this.ac_triggers.includes(ev.key)) {
+                    if (ev.key === "Tab") {
+                        ev.preventDefault();
+                    }
                     this.auto_completing = true;
                     this.auto_completing = true;
+                } else if (ev.key === "Backspace") {
+                    const word = u.getCurrentWord(ev.target, ev.target.selectionEnd-1);
+                    if (this.ac_triggers.includes(word[0])) {
+                        this.auto_completing = true;
+                    }
                 }
                 }
             }
             }
 
 
@@ -316,7 +321,7 @@ converse.plugins.add("converse-autocomplete", {
                 let value = this.match_current_word ? u.getCurrentWord(this.input) : this.input.value;
                 let value = this.match_current_word ? u.getCurrentWord(this.input) : this.input.value;
 
 
                 let ignore_min_chars = false;
                 let ignore_min_chars = false;
-                if (this.trigger_on_at && value.startsWith('@')) {
+                if (this.ac_triggers.includes(value[0]) && !this.include_triggers.includes(ev.key)) {
                     ignore_min_chars = true;
                     ignore_min_chars = true;
                     value = value.slice('1');
                     value = value.slice('1');
                 }
                 }

+ 1 - 1
src/converse-bookmarks.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-caps.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-chatboxviews.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // Copyright (c) 2012-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-chatview.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-controlbox.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-dragresize.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-embedded.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-fullscreen.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) JC Brand <jc@opkode.com>
 // Copyright (c) JC Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-headline.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-minimize.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-modal.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 13 - 13
src/converse-muc-views.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -128,7 +128,7 @@ converse.plugins.add('converse-muc-views', {
             return str;
             return str;
         }
         }
 
 
-        /* http://xmpp.org/extensions/xep-0045.html
+        /* https://xmpp.org/extensions/xep-0045.html
          * ----------------------------------------
          * ----------------------------------------
          * 100 message      Entering a groupchat         Inform user that any occupant is allowed to see the user's full JID
          * 100 message      Entering a groupchat         Inform user that any occupant is allowed to see the user's full JID
          * 101 message (out of band)                     Affiliation change  Inform user that his or her affiliation changed while not in the groupchat
          * 101 message (out of band)                     Affiliation change  Inform user that his or her affiliation changed while not in the groupchat
@@ -206,7 +206,7 @@ converse.plugins.add('converse-muc-views', {
              *  (XMLElement) stanza: The IQ stanza containing the groupchat
              *  (XMLElement) stanza: The IQ stanza containing the groupchat
              *      info.
              *      info.
              */
              */
-            // All MUC features found here: http://xmpp.org/registrar/disco-features.html
+            // All MUC features found here: https://xmpp.org/registrar/disco-features.html
             el.querySelector('span.spinner').remove();
             el.querySelector('span.spinner').remove();
             el.querySelector('a.room-info').classList.add('selected');
             el.querySelector('a.room-info').classList.add('selected');
             el.insertAdjacentHTML(
             el.insertAdjacentHTML(
@@ -506,7 +506,9 @@ converse.plugins.add('converse-muc-views', {
                 'click .upload-file': 'toggleFileUpload',
                 'click .upload-file': 'toggleFileUpload',
                 'keydown .chat-textarea': 'keyPressed',
                 'keydown .chat-textarea': 'keyPressed',
                 'keyup .chat-textarea': 'keyUp',
                 'keyup .chat-textarea': 'keyUp',
-                'input .chat-textarea': 'inputChanged'
+                'input .chat-textarea': 'inputChanged',
+                'dragover .chat-textarea': 'onDragOver',
+                'drop .chat-textarea': 'onDrop',
             },
             },
 
 
             initialize () {
             initialize () {
@@ -560,7 +562,7 @@ converse.plugins.add('converse-muc-views', {
                 this.renderHeading();
                 this.renderHeading();
                 this.renderChatArea();
                 this.renderChatArea();
                 this.renderMessageForm();
                 this.renderMessageForm();
-                this.initAutoComplete();
+                this.initMentionAutoComplete();
                 if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
                 if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
                     this.showSpinner();
                     this.showSpinner();
                 }
                 }
@@ -587,16 +589,16 @@ converse.plugins.add('converse-muc-views', {
                 return this;
                 return this;
             },
             },
 
 
-            initAutoComplete () {
+            initMentionAutoComplete () {
                 this.auto_complete = new _converse.AutoComplete(this.el, {
                 this.auto_complete = new _converse.AutoComplete(this.el, {
                     'auto_first': true,
                     'auto_first': true,
                     'auto_evaluate': false,
                     'auto_evaluate': false,
                     'min_chars': 1,
                     'min_chars': 1,
                     'match_current_word': true,
                     'match_current_word': true,
-                    'match_on_tab': true,
                     'list': () => this.model.occupants.map(o => ({'label': o.getDisplayName(), 'value': `@${o.getDisplayName()}`})),
                     'list': () => this.model.occupants.map(o => ({'label': o.getDisplayName(), 'value': `@${o.getDisplayName()}`})),
                     'filter': _converse.FILTER_STARTSWITH,
                     'filter': _converse.FILTER_STARTSWITH,
-                    'trigger_on_at': true
+                    'ac_triggers': ["Tab", "@"],
+                    'include_triggers': []
                 });
                 });
                 this.auto_complete.on('suggestion-box-selectcomplete', () => (this.auto_completing = false));
                 this.auto_complete.on('suggestion-box-selectcomplete', () => (this.auto_completing = false));
             },
             },
@@ -812,7 +814,7 @@ converse.plugins.add('converse-muc-views', {
                  * ignore <gone/> notifications in groupchats.
                  * ignore <gone/> notifications in groupchats.
                  *
                  *
                  * As laid out in the business rules in XEP-0085
                  * As laid out in the business rules in XEP-0085
-                 * http://xmpp.org/extensions/xep-0085.html#bizrules-groupchat
+                 * https://xmpp.org/extensions/xep-0085.html#bizrules-groupchat
                  */
                  */
                 if (message.get('fullname') === this.model.get('nick')) {
                 if (message.get('fullname') === this.model.get('nick')) {
                     // Don't know about other servers, but OpenFire sends
                     // Don't know about other servers, but OpenFire sends
@@ -1669,7 +1671,7 @@ converse.plugins.add('converse-muc-views', {
 
 
             showStatusMessages (stanza) {
             showStatusMessages (stanza) {
                 /* Check for status codes and communicate their purpose to the user.
                 /* Check for status codes and communicate their purpose to the user.
-                 * See: http://xmpp.org/registrar/mucstatus.html
+                 * See: https://xmpp.org/registrar/mucstatus.html
                  *
                  *
                  * Parameters:
                  * Parameters:
                  *  (XMLElement) stanza: The message or presence stanza
                  *  (XMLElement) stanza: The message or presence stanza
@@ -1837,9 +1839,7 @@ converse.plugins.add('converse-muc-views', {
                 const show = this.model.get('show');
                 const show = this.model.get('show');
                 return tpl_occupant(
                 return tpl_occupant(
                     _.extend(
                     _.extend(
-                        { '_': _, // XXX Normally this should already be included,
-                                  // but with the current webpack build,
-                                  // we only get a subset of the _ methods.
+                        { '_': _,
                           'jid': '',
                           'jid': '',
                           'show': show,
                           'show': show,
                           'hint_show': _converse.PRETTY_CHAT_STATUS[show],
                           'hint_show': _converse.PRETTY_CHAT_STATUS[show],

+ 1 - 1
src/converse-notification.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-omemo.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-profile.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-register.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-roomslist.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-rosterview.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-singleton.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/headless/converse-chatboxes.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // Copyright (c) 2012-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/headless/converse-core.js

@@ -165,7 +165,7 @@ _converse.TIMEOUTS = { // Set as module attr so that we can override in tests.
 };
 };
 
 
 // XEP-0085 Chat states
 // XEP-0085 Chat states
-// http://xmpp.org/extensions/xep-0085.html
+// https://xmpp.org/extensions/xep-0085.html
 _converse.INACTIVE = 'inactive';
 _converse.INACTIVE = 'inactive';
 _converse.ACTIVE = 'active';
 _converse.ACTIVE = 'active';
 _converse.COMPOSING = 'composing';
 _converse.COMPOSING = 'composing';

+ 2 - 2
src/headless/converse-disco.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse developers
 // Copyright (c) 2013-2019, the Converse developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -217,7 +217,7 @@ converse.plugins.add('converse-disco', {
         });
         });
 
 
         function addClientFeatures () {
         function addClientFeatures () {
-            // See http://xmpp.org/registrar/disco-categories.html
+            // See https://xmpp.org/registrar/disco-categories.html
             _converse.api.disco.own.identities.add('client', 'web', 'Converse');
             _converse.api.disco.own.identities.add('client', 'web', 'Converse');
 
 
             _converse.api.disco.own.features.add(Strophe.NS.BOSH);
             _converse.api.disco.own.features.add(Strophe.NS.BOSH);

+ 1 - 1
src/headless/converse-mam.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 5 - 5
src/headless/converse-muc.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)
@@ -538,7 +538,7 @@ converse.plugins.add('converse-muc', {
                 /* Send an IQ stanza to the server, asking it for the
                 /* Send an IQ stanza to the server, asking it for the
                  * member-list of this groupchat.
                  * member-list of this groupchat.
                  *
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+                 * See: https://xmpp.org/extensions/xep-0045.html#modifymember
                  *
                  *
                  * Parameters:
                  * Parameters:
                  *  (String) affiliation: The specific member list to
                  *  (String) affiliation: The specific member list to
@@ -559,7 +559,7 @@ converse.plugins.add('converse-muc', {
                 /* Send IQ stanzas to the server to set an affiliation for
                 /* Send IQ stanzas to the server to set an affiliation for
                  * the provided JIDs.
                  * the provided JIDs.
                  *
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+                 * See: https://xmpp.org/extensions/xep-0045.html#modifymember
                  *
                  *
                  * XXX: Prosody doesn't accept multiple JIDs' affiliations
                  * XXX: Prosody doesn't accept multiple JIDs' affiliations
                  * being set in one IQ stanza, so as a workaround we send
                  * being set in one IQ stanza, so as a workaround we send
@@ -728,7 +728,7 @@ converse.plugins.add('converse-muc', {
                 /* Send IQ stanzas to the server to modify the
                 /* Send IQ stanzas to the server to modify the
                  * affiliations in this groupchat.
                  * affiliations in this groupchat.
                  *
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+                 * See: https://xmpp.org/extensions/xep-0045.html#modifymember
                  *
                  *
                  * Parameters:
                  * Parameters:
                  *  (Object) members: A map of jids, affiliations and optionally reasons
                  *  (Object) members: A map of jids, affiliations and optionally reasons
@@ -1498,7 +1498,7 @@ converse.plugins.add('converse-muc', {
                  *     configured automatically. Currently it doesn't make sense to specify
                  *     configured automatically. Currently it doesn't make sense to specify
                  *     `roomconfig` values if `auto_configure` is set to `false`.
                  *     `roomconfig` values if `auto_configure` is set to `false`.
                  *     For a list of configuration values that can be passed in, refer to these values
                  *     For a list of configuration values that can be passed in, refer to these values
-                 *     in the [XEP-0045 MUC specification](http://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
+                 *     in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
                  *     The values should be named without the `muc#roomconfig_` prefix.
                  *     The values should be named without the `muc#roomconfig_` prefix.
                  * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
                  * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
                  *     maximized, when opened. Set to `false` by default.
                  *     maximized, when opened. Set to `false` by default.

+ 1 - 1
src/headless/converse-pubsub.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2019, the Converse.js developers
 // Copyright (c) 2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/headless/converse-roster.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/headless/converse-vcard.js

@@ -1,5 +1,5 @@
 // Converse.js
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/headless/i18n.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the internationalization module.
 // This is the internationalization module.
 //
 //

+ 6 - 4
src/headless/utils/core.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //
@@ -332,9 +332,11 @@ u.siblingIndex = function (el) {
     return i;
     return i;
 };
 };
 
 
-u.getCurrentWord = function (input) {
-    const cursor = input.selectionEnd || undefined;
-    return _.last(input.value.slice(0, cursor).split(' '));
+u.getCurrentWord = function (input, index) {
+    if (!index) {
+        index = input.selectionEnd || undefined;
+    }
+    return _.last(input.value.slice(0, index).split(' '));
 };
 };
 
 
 u.replaceCurrentWord = function (input, new_value) {
 u.replaceCurrentWord = function (input, new_value) {

+ 1 - 1
src/headless/utils/form.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //

+ 1 - 1
src/headless/utils/muc.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is the utilities module.
 // This is the utilities module.
 //
 //

+ 79 - 83
src/utils/html.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 //
 // This is a form utilities module.
 // This is a form utilities module.
 //
 //
@@ -539,89 +539,85 @@ u.xForm2webForm = function (field, stanza, domain) {
      *  Parameters:
      *  Parameters:
      *      (XMLElement) field - the field to convert
      *      (XMLElement) field - the field to convert
      */
      */
-    if (field.getAttribute('type')) {
-        if (field.getAttribute('type') === 'list-single' ||
-            field.getAttribute('type') === 'list-multi') {
-
-            const values = _.map(
-                u.queryChildren(field, 'value'),
-                _.partial(_.get, _, 'textContent')
-            );
-            const options = _.map(
-                u.queryChildren(field, 'option'),
-                function (option) {
-                    const value = _.get(option.querySelector('value'), 'textContent');
-                    return tpl_select_option({
-                        'value': value,
-                        'label': option.getAttribute('label'),
-                        'selected': _.includes(values, value),
-                        'required': !_.isNil(field.querySelector('required'))
-                    })
-                }
-            );
-            return tpl_form_select({
-                'id': u.getUniqueId(),
-                'name': field.getAttribute('var'),
-                'label': field.getAttribute('label'),
-                'options': options.join(''),
-                'multiple': (field.getAttribute('type') === 'list-multi'),
-                'required': !_.isNil(field.querySelector('required'))
-            });
-        } else if (field.getAttribute('type') === 'fixed') {
-            const text = _.get(field.querySelector('value'), 'textContent');
-            return '<p class="form-help">'+text+'</p>';
-        } else if (field.getAttribute('type') === 'jid-multi') {
-            return tpl_form_textarea({
-                'name': field.getAttribute('var'),
-                'label': field.getAttribute('label') || '',
-                'value': _.get(field.querySelector('value'), 'textContent'),
-                'required': !_.isNil(field.querySelector('required'))
-            });
-        } else if (field.getAttribute('type') === 'boolean') {
-            return tpl_form_checkbox({
-                'id': u.getUniqueId(),
-                'name': field.getAttribute('var'),
-                'label': field.getAttribute('label') || '',
-                'checked': _.get(field.querySelector('value'), 'textContent') === "1" && 'checked="1"' || '',
-                'required': !_.isNil(field.querySelector('required'))
-            });
-        } else if (field.getAttribute('var') === 'url') {
-            return tpl_form_url({
-                'label': field.getAttribute('label') || '',
-                'value': _.get(field.querySelector('value'), 'textContent')
-            });
-        } else if (field.getAttribute('var') === 'username') {
-            return tpl_form_username({
-                'domain': ' @'+domain,
-                'name': field.getAttribute('var'),
-                'type': XFORM_TYPE_MAP[field.getAttribute('type')],
-                'label': field.getAttribute('label') || '',
-                'value': _.get(field.querySelector('value'), 'textContent'),
-                'required': !_.isNil(field.querySelector('required'))
-            });
-        } else {
-            return tpl_form_input({
-                'id': u.getUniqueId(),
-                'label': field.getAttribute('label') || '',
-                'name': field.getAttribute('var'),
-                'placeholder': null,
-                'required': !_.isNil(field.querySelector('required')),
-                'type': XFORM_TYPE_MAP[field.getAttribute('type')],
-                'value': _.get(field.querySelector('value'), 'textContent')
-            });
-        }
+    if (field.getAttribute('type') === 'list-single' ||
+        field.getAttribute('type') === 'list-multi') {
+
+        const values = _.map(
+            u.queryChildren(field, 'value'),
+            _.partial(_.get, _, 'textContent')
+        );
+        const options = _.map(
+            u.queryChildren(field, 'option'),
+            function (option) {
+                const value = _.get(option.querySelector('value'), 'textContent');
+                return tpl_select_option({
+                    'value': value,
+                    'label': option.getAttribute('label'),
+                    'selected': _.includes(values, value),
+                    'required': !_.isNil(field.querySelector('required'))
+                })
+            }
+        );
+        return tpl_form_select({
+            'id': u.getUniqueId(),
+            'name': field.getAttribute('var'),
+            'label': field.getAttribute('label'),
+            'options': options.join(''),
+            'multiple': (field.getAttribute('type') === 'list-multi'),
+            'required': !_.isNil(field.querySelector('required'))
+        });
+    } else if (field.getAttribute('type') === 'fixed') {
+        const text = _.get(field.querySelector('value'), 'textContent');
+        return '<p class="form-help">'+text+'</p>';
+    } else if (field.getAttribute('type') === 'jid-multi') {
+        return tpl_form_textarea({
+            'name': field.getAttribute('var'),
+            'label': field.getAttribute('label') || '',
+            'value': _.get(field.querySelector('value'), 'textContent'),
+            'required': !_.isNil(field.querySelector('required'))
+        });
+    } else if (field.getAttribute('type') === 'boolean') {
+        return tpl_form_checkbox({
+            'id': u.getUniqueId(),
+            'name': field.getAttribute('var'),
+            'label': field.getAttribute('label') || '',
+            'checked': _.get(field.querySelector('value'), 'textContent') === "1" && 'checked="1"' || '',
+            'required': !_.isNil(field.querySelector('required'))
+        });
+    } else if (field.getAttribute('var') === 'url') {
+        return tpl_form_url({
+            'label': field.getAttribute('label') || '',
+            'value': _.get(field.querySelector('value'), 'textContent')
+        });
+    } else if (field.getAttribute('var') === 'username') {
+        return tpl_form_username({
+            'domain': ' @'+domain,
+            'name': field.getAttribute('var'),
+            'type': XFORM_TYPE_MAP[field.getAttribute('type')],
+            'label': field.getAttribute('label') || '',
+            'value': _.get(field.querySelector('value'), 'textContent'),
+            'required': !_.isNil(field.querySelector('required'))
+        });
+    } else if (field.getAttribute('var') === 'ocr') { // Captcha
+        const uri = field.querySelector('uri');
+        const el = sizzle('data[cid="'+uri.textContent.replace(/^cid:/, '')+'"]', stanza)[0];
+        return tpl_form_captcha({
+            'label': field.getAttribute('label'),
+            'name': field.getAttribute('var'),
+            'data': _.get(el, 'textContent'),
+            'type': uri.getAttribute('type'),
+            'required': !_.isNil(field.querySelector('required'))
+        });
     } else {
     } else {
-        if (field.getAttribute('var') === 'ocr') { // Captcha
-            const uri = field.querySelector('uri');
-            const el = sizzle('data[cid="'+uri.textContent.replace(/^cid:/, '')+'"]', stanza)[0];
-            return tpl_form_captcha({
-                'label': field.getAttribute('label'),
-                'name': field.getAttribute('var'),
-                'data': _.get(el, 'textContent'),
-                'type': uri.getAttribute('type'),
-                'required': !_.isNil(field.querySelector('required'))
-            });
-        }
+        return tpl_form_input({
+            'id': u.getUniqueId(),
+            'label': field.getAttribute('label') || '',
+            'name': field.getAttribute('var'),
+            'placeholder': null,
+            'required': !_.isNil(field.querySelector('required')),
+            'type': XFORM_TYPE_MAP[field.getAttribute('type')],
+            'value': _.get(field.querySelector('value'), 'textContent')
+        });
     }
     }
 }
 }
 export default u;
 export default u;

+ 1 - 1
tests/utils.js

@@ -182,7 +182,7 @@
 
 
         // The user has just entered the room (because join was called)
         // The user has just entered the room (because join was called)
         // and receives their own presence from the server.
         // and receives their own presence from the server.
-        // See example 24: http://xmpp.org/extensions/xep-0045.html#enter-pres
+        // See example 24: https://xmpp.org/extensions/xep-0045.html#enter-pres
         var presence = $pres({
         var presence = $pres({
                 to: _converse.connection.jid,
                 to: _converse.connection.jid,
                 from: `${room_jid}/${nick}`,
                 from: `${room_jid}/${nick}`,