2
0
Эх сурвалжийг харах

Merge branch 'master' into master

JC Brand 6 жил өмнө
parent
commit
2de794abc1
51 өөрчлөгдсөн 415 нэмэгдсэн , 365 устгасан
  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
 =======================
 
-Thanks for contributing to `Converse.js <http://conversejs.org>`_.
+Thanks for contributing to `Converse.js <https://conversejs.org>`_.
 
 Support questions
 =================

+ 5 - 2
CHANGES.md

@@ -2,8 +2,10 @@
 
 ## 4.1.3 (Unreleased)
 
+- Updated translation: lt
 - Upgrade to Backbone 1.4.0
 - 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`
 - 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)
@@ -13,7 +15,8 @@
 - #1400: When a chat message is just an emoji, enlarge the emoji
 - #1437: List of groupchats in modal doesn't scroll
 - #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)
 
@@ -647,7 +650,7 @@ More info here: https://github.com/LeaVerou/awesomplete/pull/17082
 ## 2.0.4 (2016-12-13)
 - #737: Bugfix. Translations weren't being applied. [jcbrand]
 - 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. 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

+ 1 - 1
RELEASE.md

@@ -8,7 +8,7 @@
 6. `git commit -am "New release 5.0.0"`
 7. `git tag -s v5.0.0
 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.
     * Create a new version for the CDN by copying
     * Check out the correct tag

+ 1 - 1
composer.json

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

+ 1 - 1
css/converse.css

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

+ 143 - 137
dist/converse.js

@@ -47933,7 +47933,7 @@ exports["filterCSS"] = (filterCSS);
 __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");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -48002,14 +48002,15 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
         _.assignIn(this, {
           '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,
           'max_items': 10,
           'auto_evaluate': true,
           'auto_first': false,
+          // Should the first element be automatically selected?
           'data': _.identity,
           'filter': _converse.FILTER_CONTAINS,
           'sort': config.sort === false ? false : SORT_BYLENGTH,
@@ -48241,11 +48242,18 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
           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;
+        } 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 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;
           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___default = /*#__PURE__*/__webpack_require__.n(templates_chatroom_bookmark_toggle_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_6__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_22__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_login_panel_html__WEBPACK_IMPORTED_MODULE_10__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_dragresize_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_inverse_brand_heading_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) JC Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_chatbox_html__WEBPACK_IMPORTED_MODULE_2__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_trimmed_chat_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_alert_modal_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(xss__WEBPACK_IMPORTED_MODULE_26__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -53528,7 +53536,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
       */
       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
      * 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
        *      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('a.room-info').classList.add('selected');
       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',
         'keydown .chat-textarea': 'keyPressed',
         'keyup .chat-textarea': 'keyUp',
-        'input .chat-textarea': 'inputChanged'
+        'input .chat-textarea': 'inputChanged',
+        'dragover .chat-textarea': 'onDragOver',
+        'drop .chat-textarea': 'onDrop'
       },
 
       initialize() {
@@ -53976,7 +53986,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         this.renderHeading();
         this.renderChatArea();
         this.renderMessageForm();
-        this.initAutoComplete();
+        this.initMentionAutoComplete();
 
         if (this.model.get('connection_status') !== _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED) {
           this.showSpinner();
@@ -54006,19 +54016,19 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         return this;
       },
 
-      initAutoComplete() {
+      initMentionAutoComplete() {
         this.auto_complete = new _converse.AutoComplete(this.el, {
           'auto_first': true,
           'auto_evaluate': false,
           'min_chars': 1,
           'match_current_word': true,
-          'match_on_tab': true,
           'list': () => this.model.occupants.map(o => ({
             'label': o.getDisplayName(),
             'value': `@${o.getDisplayName()}`
           })),
           'filter': _converse.FILTER_STARTSWITH,
-          'trigger_on_at': true
+          'ac_triggers': ["Tab", "@"],
+          'include_triggers': []
         });
         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.
          *
          * 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')) {
           // 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) {
         /* 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:
          *  (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');
         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': '',
           'show': 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__);
 /* 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_toolbar_omemo_html__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // 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 _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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_rooms_list_item_html__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // 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___default = /*#__PURE__*/__webpack_require__.n(templates_search_contact_html__WEBPACK_IMPORTED_MODULE_13__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(filesize__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -63029,7 +63036,7 @@ _converse.TIMEOUTS = {
   'PAUSED': 10000,
   'INACTIVE': 90000
 }; // XEP-0085 Chat states
-// http://xmpp.org/extensions/xep-0085.html
+// https://xmpp.org/extensions/xep-0085.html
 
 _converse.INACTIVE = 'inactive';
 _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___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -65024,7 +65031,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
     });
 
     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.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___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_3__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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; }
 
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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
          * member-list of this groupchat.
          *
-         * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+         * See: https://xmpp.org/extensions/xep-0045.html#modifymember
          *
          * Parameters:
          *  (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
          * 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
          * 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
          * affiliations in this groupchat.
          *
-         * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+         * See: https://xmpp.org/extensions/xep-0045.html#modifymember
          *
          * Parameters:
          *  (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
          *     `roomconfig` values if `auto_configure` is set to `false`.
          *     For a list of configuration values that can be passed in, refer to these values
-         *     in the [XEP-0045 MUC specification](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.
          * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
          *     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_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./converse-core */ "./src/headless/converse-core.js");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2019, the Converse.js developers
 // 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__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(_templates_vcard_html__WEBPACK_IMPORTED_MODULE_1__);
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_30__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // 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___default = /*#__PURE__*/__webpack_require__.n(sizzle__WEBPACK_IMPORTED_MODULE_4__);
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // This is the utilities module.
 //
@@ -70420,9 +70427,12 @@ u.siblingIndex = function (el) {
   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) {
@@ -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 _core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // 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 _core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core */ "./src/headless/utils/core.js");
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // 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 _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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // This is a form utilities module.
 //
@@ -96168,86 +96178,82 @@ _headless_utils_core__WEBPACK_IMPORTED_MODULE_16__["default"].xForm2webForm = fu
    *  Parameters:
    *      (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'))
       });
-    } 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 {
-    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``
 
-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.
 
@@ -572,7 +572,7 @@ csi_waiting_time
 
 * 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
 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
     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
-    `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".
 
 .. _`locales`:
@@ -993,7 +993,7 @@ muc_instant_rooms
 
 Determines whether 'instant' (also called 'dynamic' in OpenFire) rooms are created.
 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
 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
   ``roomconfig`` values if ``auto_configure`` is set to ``false``.
   For a list of configuration values that can be passed in, refer to these values
-  in the `XEP-0045 MUC specification <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.
 * *maximize*: A boolean, indicating whether minimized rooms should also be
   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
 ============
 
-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.
 
 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
 ==============
 
-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
 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.
 
 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`:
 
@@ -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
 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>`_,
 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>`_
 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.
 
 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
-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.
 
 Once authenticated, it receives RID and SID tokens which need to be passed

+ 1 - 1
mockup/index.html

@@ -11,7 +11,7 @@
 <body>
     <div id="header_wrap" class="outer">
         <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>
         </header>
     </div>

+ 1 - 1
sass/converse.scss

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

+ 80 - 39
spec/autocomplete.js

@@ -18,45 +18,44 @@
         it("shows all autocompletion options when the user presses @",
             mock.initConverse(
                 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",
@@ -88,7 +87,8 @@
                 'target': textarea,
                 'preventDefault': _.noop,
                 'stopPropagation': _.noop,
-                'keyCode': 9
+                'keyCode': 9,
+                'key': 'Tab'
             }
             view.keyPressed(tab_event);
             view.keyUp(tab_event);
@@ -168,5 +168,46 @@
             expect(textarea.value).toBe('hello @z3r0 ');
             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');
                         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');
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
@@ -817,7 +817,7 @@
                         test_utils.openControlBox();
                         await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
                         // 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();
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const view = await test_utils.openChatBoxFor(_converse, sender_jid);
@@ -985,7 +985,7 @@
                         await test_utils.waitForRoster(_converse, 'current');
                         test_utils.openControlBox();
                         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');
                         await test_utils.openChatBoxFor(_converse, 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)
                 // and receives their own presence from the server.
                 // 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">
                  *    <x xmlns="http://jabber.org/protocol/muc#user">
@@ -1219,7 +1219,7 @@
 
                 /* Check that an IQ is sent out, asking for the
                  * 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'
                  *      id='config1'
@@ -1234,7 +1234,7 @@
                     `</iq>`);
 
                 /* 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',
                     'id': IQ_id,
@@ -1398,7 +1398,7 @@
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                     name = mock.chatroom_names[i];
                     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 =
                     presence = $pres({
                             to:'dummy@localhost/pda',
@@ -1418,11 +1418,11 @@
                 }
 
                 // 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--) {
                     name = mock.chatroom_names[i];
                     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({
                         to:'dummy@localhost/pda',
                         from:'lounge@localhost/'+name,
@@ -1452,7 +1452,7 @@
                 for (var i=0; i<mock.chatroom_names.length; i++) {
                     name = mock.chatroom_names[i];
                     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 =
                     presence = $pres({
                             to:'dummy@localhost/pda',
@@ -1472,11 +1472,11 @@
                 }
 
                 // 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--) {
                     name = mock.chatroom_names[i];
                     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({
                         to:'dummy@localhost/pda',
                         from:'lounge@localhost/'+name,
@@ -1695,7 +1695,7 @@
                 // The user has just entered the groupchat (because join was called)
                 // and receives their own presence from the server.
                 // See example 24:
-                // http://xmpp.org/extensions/xep-0045.html#enter-pres
+                // https://xmpp.org/extensions/xep-0045.html#enter-pres
                 const presence = $pres({
                         to:'dummy@localhost/resource',
                         from:'lounge@localhost/thirdwitch',
@@ -1946,7 +1946,7 @@
                     async function (done, _converse) {
 
                 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(`
                     <message xmlns="jabber:client" to="jc@opkode.com/_converse.js-60429116" type="groupchat" from="jdev@conference.jabber.org/ralphm">
                         <subject>${text}</subject>
@@ -2008,7 +2008,7 @@
                  * nickname and one indicating availability for the new
                  * nickname.
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#changenick
+                 * See: https://xmpp.org/extensions/xep-0045.html#changenick
                  *
                  *  <presence
                  *      from='coven@localhost/thirdwitch'
@@ -4082,7 +4082,7 @@
                 spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
                 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);
 
                 const server_input = modal.el.querySelector('input[name="server"]');
@@ -4299,7 +4299,7 @@
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                         .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
                     let msg = $msg({
@@ -4368,10 +4368,8 @@
                     expect(notifications.length).toBe(2);
                     expect(notifications[0].textContent).toEqual('newguy 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({
                         from: `${room_jid}/some1`,
                         id: (new Date()).getTime(),
@@ -4386,8 +4384,7 @@
                     expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                     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]();
                     events = view.el.querySelectorAll('.chat-event');
                     expect(events.length).toBe(3);
@@ -4475,7 +4472,7 @@
                     expect(sizzle('div.chat-info:last', chat_content).pop().textContent)
                         .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
                     var msg = $msg({

+ 1 - 1
spec/room_registration.js

@@ -112,7 +112,7 @@
                 }).then(stanza => {
                     // The user has just entered the room (because join was called)
                     // 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({
                             to: _converse.connection.jid,
                             from: room_jid,

+ 1 - 1
spec/roster.js

@@ -1270,7 +1270,7 @@
                     expect(_converse.roster.pluck('jid').length).toBe(1);
                     expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
                     // 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({
                         to: _converse.connection.jid,
                         type: 'result',

+ 15 - 10
src/converse-autocomplete.js

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -13,7 +13,6 @@ import converse from "@converse/headless/converse-core";
 const { _, Backbone } = converse.env,
       u = converse.env.utils;
 
-
 converse.plugins.add("converse-autocomplete", {
 
     initialize () {
@@ -73,12 +72,12 @@ converse.plugins.add("converse-autocomplete", {
 
                 _.assignIn(this, {
                     '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,
                     'max_items': 10,
                     'auto_evaluate': true,
-                    'auto_first': false,
+                    'auto_first': false, // Should the first element be automatically selected?
                     'data': _.identity,
                     'filter': _converse.FILTER_CONTAINS,
                     'sort': config.sort === false ? false : SORT_BYLENGTH,
@@ -291,11 +290,17 @@ converse.plugins.add("converse-autocomplete", {
                         , ev.keyCode)) {
                     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;
+                } 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 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;
                     value = value.slice('1');
                 }

+ 1 - 1
src/converse-bookmarks.js

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

+ 1 - 1
src/converse-caps.js

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

+ 1 - 1
src/converse-chatboxviews.js

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

+ 1 - 1
src/converse-chatview.js

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-embedded.js

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) JC Brand <jc@opkode.com>
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-modal.js

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

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

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -128,7 +128,7 @@ converse.plugins.add('converse-muc-views', {
             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
          * 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
              *      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('a.room-info').classList.add('selected');
             el.insertAdjacentHTML(
@@ -506,7 +506,9 @@ converse.plugins.add('converse-muc-views', {
                 'click .upload-file': 'toggleFileUpload',
                 'keydown .chat-textarea': 'keyPressed',
                 'keyup .chat-textarea': 'keyUp',
-                'input .chat-textarea': 'inputChanged'
+                'input .chat-textarea': 'inputChanged',
+                'dragover .chat-textarea': 'onDragOver',
+                'drop .chat-textarea': 'onDrop',
             },
 
             initialize () {
@@ -560,7 +562,7 @@ converse.plugins.add('converse-muc-views', {
                 this.renderHeading();
                 this.renderChatArea();
                 this.renderMessageForm();
-                this.initAutoComplete();
+                this.initMentionAutoComplete();
                 if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
                     this.showSpinner();
                 }
@@ -587,16 +589,16 @@ converse.plugins.add('converse-muc-views', {
                 return this;
             },
 
-            initAutoComplete () {
+            initMentionAutoComplete () {
                 this.auto_complete = new _converse.AutoComplete(this.el, {
                     'auto_first': true,
                     'auto_evaluate': false,
                     'min_chars': 1,
                     'match_current_word': true,
-                    'match_on_tab': true,
                     'list': () => this.model.occupants.map(o => ({'label': o.getDisplayName(), 'value': `@${o.getDisplayName()}`})),
                     'filter': _converse.FILTER_STARTSWITH,
-                    'trigger_on_at': true
+                    'ac_triggers': ["Tab", "@"],
+                    'include_triggers': []
                 });
                 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.
                  *
                  * 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')) {
                     // Don't know about other servers, but OpenFire sends
@@ -1669,7 +1671,7 @@ converse.plugins.add('converse-muc-views', {
 
             showStatusMessages (stanza) {
                 /* 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:
                  *  (XMLElement) stanza: The message or presence stanza
@@ -1837,9 +1839,7 @@ converse.plugins.add('converse-muc-views', {
                 const show = this.model.get('show');
                 return tpl_occupant(
                     _.extend(
-                        { '_': _, // XXX Normally this should already be included,
-                                  // but with the current webpack build,
-                                  // we only get a subset of the _ methods.
+                        { '_': _,
                           'jid': '',
                           'show': 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, JC Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-omemo.js

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, Jan-Carel Brand <jc@opkode.com>
 // Licensed under the Mozilla Public License (MPLv2)

+ 1 - 1
src/converse-rosterview.js

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

+ 1 - 1
src/converse-singleton.js

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

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

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2012-2019, the Converse.js developers
 // 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
-// http://xmpp.org/extensions/xep-0085.html
+// https://xmpp.org/extensions/xep-0085.html
 _converse.INACTIVE = 'inactive';
 _converse.ACTIVE = 'active';
 _converse.COMPOSING = 'composing';

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

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse developers
 // Licensed under the Mozilla Public License (MPLv2)
@@ -217,7 +217,7 @@ converse.plugins.add('converse-disco', {
         });
 
         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.features.add(Strophe.NS.BOSH);

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

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

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

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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
                  * member-list of this groupchat.
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+                 * See: https://xmpp.org/extensions/xep-0045.html#modifymember
                  *
                  * Parameters:
                  *  (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
                  * 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
                  * 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
                  * affiliations in this groupchat.
                  *
-                 * See: http://xmpp.org/extensions/xep-0045.html#modifymember
+                 * See: https://xmpp.org/extensions/xep-0045.html#modifymember
                  *
                  * Parameters:
                  *  (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
                  *     `roomconfig` values if `auto_configure` is set to `false`.
                  *     For a list of configuration values that can be passed in, refer to these values
-                 *     in the [XEP-0045 MUC specification](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.
                  * @param {boolean} [attrs.maximize] A boolean, indicating whether minimized rooms should also be
                  *     maximized, when opened. Set to `false` by default.

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

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

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

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

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

@@ -1,5 +1,5 @@
 // Converse.js
-// http://conversejs.org
+// https://conversejs.org
 //
 // Copyright (c) 2013-2019, the Converse.js developers
 // 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)
-// http://conversejs.org
+// https://conversejs.org
 //
 // This is the internationalization module.
 //

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

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // This is the utilities module.
 //
@@ -332,9 +332,11 @@ u.siblingIndex = function (el) {
     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) {

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

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

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

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

+ 79 - 83
src/utils/html.js

@@ -1,5 +1,5 @@
 // Converse.js (A browser based XMPP chat client)
-// http://conversejs.org
+// https://conversejs.org
 //
 // This is a form utilities module.
 //
@@ -539,89 +539,85 @@ u.xForm2webForm = function (field, stanza, domain) {
      *  Parameters:
      *      (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 {
-        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;

+ 1 - 1
tests/utils.js

@@ -182,7 +182,7 @@
 
         // The user has just entered the room (because join was called)
         // 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({
                 to: _converse.connection.jid,
                 from: `${room_jid}/${nick}`,