Browse Source

Remove circular dependencies

Most have been removed, but there are still some left. Looks like
removing those might need more refactoring and potentially a major
version update.

Also use relative imports in `@converse/headless`.

Updates #3243
JC Brand 1 year ago
parent
commit
23024be5f0
78 changed files with 287 additions and 239 deletions
  1. 13 0
      package-lock.json
  2. 1 0
      package.json
  3. 3 3
      src/headless/log.js
  4. 2 1
      src/headless/plugins/adhoc/api.js
  5. 1 1
      src/headless/plugins/adhoc/index.js
  6. 2 2
      src/headless/plugins/adhoc/utils.js
  7. 4 3
      src/headless/plugins/bookmarks/collection.js
  8. 3 2
      src/headless/plugins/bookmarks/index.js
  9. 1 1
      src/headless/plugins/bookmarks/model.js
  10. 2 1
      src/headless/plugins/bookmarks/utils.js
  11. 4 3
      src/headless/plugins/bosh.js
  12. 1 1
      src/headless/plugins/caps/index.js
  13. 3 2
      src/headless/plugins/caps/utils.js
  14. 2 1
      src/headless/plugins/chat/api.js
  15. 2 1
      src/headless/plugins/chat/index.js
  16. 2 1
      src/headless/plugins/chat/message.js
  17. 6 5
      src/headless/plugins/chat/model.js
  18. 6 6
      src/headless/plugins/chat/parsers.js
  19. 5 3
      src/headless/plugins/chat/utils.js
  20. 2 1
      src/headless/plugins/chatboxes/api.js
  21. 3 2
      src/headless/plugins/chatboxes/chatboxes.js
  22. 2 1
      src/headless/plugins/chatboxes/index.js
  23. 2 1
      src/headless/plugins/chatboxes/utils.js
  24. 2 1
      src/headless/plugins/disco/api.js
  25. 3 1
      src/headless/plugins/disco/entity.js
  26. 3 2
      src/headless/plugins/disco/index.js
  27. 2 1
      src/headless/plugins/disco/utils.js
  28. 2 1
      src/headless/plugins/emoji/index.js
  29. 1 1
      src/headless/plugins/emoji/utils.js
  30. 2 1
      src/headless/plugins/headlines/api.js
  31. 2 2
      src/headless/plugins/headlines/feed.js
  32. 2 1
      src/headless/plugins/headlines/index.js
  33. 7 5
      src/headless/plugins/headlines/utils.js
  34. 5 3
      src/headless/plugins/mam/api.js
  35. 3 2
      src/headless/plugins/mam/index.js
  36. 2 4
      src/headless/plugins/mam/placeholder.js
  37. 5 4
      src/headless/plugins/mam/utils.js
  38. 4 2
      src/headless/plugins/muc/affiliations/utils.js
  39. 2 1
      src/headless/plugins/muc/api.js
  40. 2 4
      src/headless/plugins/muc/index.js
  41. 2 1
      src/headless/plugins/muc/message.js
  42. 2 1
      src/headless/plugins/muc/muc.js
  43. 3 2
      src/headless/plugins/muc/occupants.js
  44. 3 2
      src/headless/plugins/muc/parsers.js
  45. 4 2
      src/headless/plugins/muc/utils.js
  46. 2 1
      src/headless/plugins/ping/api.js
  47. 1 1
      src/headless/plugins/ping/index.js
  48. 2 1
      src/headless/plugins/ping/utils.js
  49. 2 1
      src/headless/plugins/pubsub.js
  50. 2 1
      src/headless/plugins/roster/api.js
  51. 3 2
      src/headless/plugins/roster/contact.js
  52. 3 2
      src/headless/plugins/roster/contacts.js
  53. 3 2
      src/headless/plugins/roster/index.js
  54. 2 2
      src/headless/plugins/roster/presence.js
  55. 5 4
      src/headless/plugins/roster/utils.js
  56. 1 1
      src/headless/plugins/smacks/index.js
  57. 2 1
      src/headless/plugins/smacks/utils.js
  58. 3 2
      src/headless/plugins/status/index.js
  59. 2 1
      src/headless/plugins/status/status.js
  60. 3 2
      src/headless/plugins/status/utils.js
  61. 2 1
      src/headless/plugins/vcard/api.js
  62. 3 2
      src/headless/plugins/vcard/index.js
  63. 4 3
      src/headless/plugins/vcard/utils.js
  64. 1 1
      src/headless/plugins/vcard/vcard.js
  65. 2 1
      src/headless/shared/actions.js
  66. 2 0
      src/headless/shared/api/index.js
  67. 2 2
      src/headless/shared/api/presence.js
  68. 1 1
      src/headless/shared/chat/utils.js
  69. 2 1
      src/headless/shared/connection/index.js
  70. 7 6
      src/headless/shared/parsers.js
  71. 2 1
      src/headless/shared/rsm.js
  72. 2 2
      src/headless/shared/settings/utils.js
  73. 18 105
      src/headless/utils/core.js
  74. 57 0
      src/headless/utils/form.js
  75. 2 1
      src/headless/utils/url.js
  76. 1 1
      src/shared/directives/styling.js
  77. 1 1
      src/shared/styling.js
  78. 7 0
      webpack/webpack.build.js

+ 13 - 0
package-lock.json

@@ -39,6 +39,7 @@
         "autoprefixer": "^10.4.5",
         "babel-loader": "^9.1.0",
         "bootstrap.native-loader": "2.0.0",
+        "circular-dependency-plugin": "^5.2.2",
         "clean-css-cli": "^5.6.2",
         "copy-webpack-plugin": "^11.0.0",
         "css-loader": "^6.7.1",
@@ -3641,6 +3642,18 @@
       "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
       "dev": true
     },
+    "node_modules/circular-dependency-plugin": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz",
+      "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      },
+      "peerDependencies": {
+        "webpack": ">=4.0.1"
+      }
+    },
     "node_modules/cjk-regex": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/cjk-regex/-/cjk-regex-2.0.1.tgz",

+ 1 - 0
package.json

@@ -76,6 +76,7 @@
     "autoprefixer": "^10.4.5",
     "babel-loader": "^9.1.0",
     "bootstrap.native-loader": "2.0.0",
+    "circular-dependency-plugin": "^5.2.2",
     "clean-css-cli": "^5.6.2",
     "copy-webpack-plugin": "^11.0.0",
     "css-loader": "^6.7.1",

+ 3 - 3
src/headless/log.js

@@ -1,4 +1,4 @@
-import { isElement } from './utils/core.js';
+import { isElement } from './utils/html.js';
 
 const LEVELS = {
     'debug': 0,
@@ -27,7 +27,7 @@ export default {
     /**
      * The the log-level, which determines how verbose the logging is.
      * @method log#setLogLevel
-     * @param { number } level - The loglevel which allows for filtering of log messages
+     * @param { string } level - The loglevel which allows for filtering of log messages
      */
     setLogLevel (level) {
         if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
@@ -44,7 +44,7 @@ export default {
      * logged as well.
      * @method log#log
      * @param { string | Error } message - The message to be logged
-     * @param { number } level - The loglevel which allows for filtering of log messages
+     * @param { string } level - The loglevel which allows for filtering of log messages
      */
     log (message, level, style='') {
         if (LEVELS[level] < LEVELS[this.loglevel]) {

+ 2 - 1
src/headless/plugins/adhoc/api.js

@@ -1,5 +1,6 @@
 import log from '../../log.js';
-import { _converse, api, converse } from "@converse/headless";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { getCommandFields, parseForCommands } from './utils.js';
 
 const { Strophe, $iq, u, stx } = converse.env;

+ 1 - 1
src/headless/plugins/adhoc/index.js

@@ -1,5 +1,5 @@
 import adhoc_api from './api.js';
-import { converse } from "@converse/headless";
+import { converse } from '../../shared/api/index.js';
 
 const { Strophe } = converse.env;
 

+ 2 - 2
src/headless/plugins/adhoc/utils.js

@@ -1,6 +1,6 @@
 import sizzle from 'sizzle';
-import { converse } from "@converse/headless";
-import { getAttributes } from '@converse/headless/shared/parsers';
+import { converse } from '../../shared/api/index.js';
+import { getAttributes } from '../../shared/parsers';
 
 const { Strophe, u } = converse.env;
 

+ 4 - 3
src/headless/plugins/bookmarks/collection.js

@@ -1,9 +1,10 @@
-import "@converse/headless/plugins/muc/index.js";
+import "../../plugins/muc/index.js";
 import Bookmark from './model.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
-import { _converse, api, converse } from "@converse/headless";
 import { getOpenPromise } from '@converse/openpromise';
-import { initStorage } from '@converse/headless/utils/storage.js';
+import { initStorage } from '../../utils/storage.js';
 
 const { Strophe, $iq, sizzle } = converse.env;
 

+ 3 - 2
src/headless/plugins/bookmarks/index.js

@@ -4,11 +4,12 @@
  * @copyright 2022, the Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
-import "@converse/headless/plugins/muc/index.js";
+import "../..//plugins/muc/index.js";
 import Bookmark from './model.js';
 import Bookmarks from './collection.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Collection } from "@converse/skeletor/src/collection.js";
-import { _converse, api, converse } from "@converse/headless";
 import { initBookmarks, getNicknameFromBookmark, handleBookmarksPush } from './utils.js';
 
 const { Strophe } = converse.env;

+ 1 - 1
src/headless/plugins/bookmarks/model.js

@@ -1,4 +1,4 @@
-import { converse } from '@converse/headless';
+import { converse } from '../../shared/api/index.js';
 import { Model } from '@converse/skeletor/src/model.js';
 
 const { Strophe } = converse.env;

+ 2 - 1
src/headless/plugins/bookmarks/utils.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
-import { _converse, api, converse } from '@converse/headless';
 
 const { Strophe, sizzle } = converse.env;
 

+ 4 - 3
src/headless/plugins/bosh.js

@@ -4,11 +4,12 @@
  * @description Converse.js plugin which add support for XEP-0206: XMPP Over BOSH
  */
 import 'strophe.js/src/bosh';
+import _converse from '../shared/_converse.js';
+import api, { converse } from '../shared/api/index.js';
 import log from "../log.js";
-import { BOSH_WAIT } from '@converse/headless/shared/constants.js';
+import { BOSH_WAIT } from '../shared/constants.js';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse, api, converse } from "../index.js";
-import { setUserJID, } from '@converse/headless/utils/init.js';
+import { setUserJID, } from '../utils/init.js';
 
 const { Strophe } = converse.env;
 

+ 1 - 1
src/headless/plugins/caps/index.js

@@ -2,7 +2,7 @@
  * @copyright 2022, the Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
-import { api, converse } from '@converse/headless';
+import api, { converse } from '../../shared/api/index.js';
 import { addCapsNode } from './utils.js';
 
 const { Strophe } = converse.env;

+ 3 - 2
src/headless/plugins/caps/utils.js

@@ -1,5 +1,6 @@
-import { _converse, converse } from '@converse/headless';
-import { arrayBufferToBase64, stringToArrayBuffer  } from '@converse/headless/utils/arraybuffer.js';
+import _converse from '../../shared/_converse.js';
+import { converse } from '../../shared/api/index.js';
+import { arrayBufferToBase64, stringToArrayBuffer  } from '../../utils/arraybuffer.js';
 
 const { Strophe, $build } = converse.env;
 

+ 2 - 1
src/headless/plugins/chat/api.js

@@ -1,4 +1,5 @@
-import { _converse, api } from "../../index.js";
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
 import log from "../../log.js";
 
 

+ 2 - 1
src/headless/plugins/chat/index.js

@@ -5,9 +5,10 @@
 import ChatBox from './model.js';
 import MessageMixin from './message.js';
 import ModelWithContact from './model-with-contact.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import chat_api from './api.js';
 import { Collection } from '@converse/skeletor/src/collection';
-import { _converse, api, converse } from '../../index.js';
 import {
     autoJoinChats,
     enableCarbons,

+ 2 - 1
src/headless/plugins/chat/message.js

@@ -1,7 +1,8 @@
 import ModelWithContact from './model-with-contact.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import dayjs from 'dayjs';
 import log from '../../log.js';
-import { _converse, api, converse } from '../../index.js';
 import { getOpenPromise } from '@converse/openpromise';
 
 const { Strophe, sizzle, u } = converse.env;

+ 6 - 5
src/headless/plugins/chat/model.js

@@ -2,17 +2,18 @@ import ModelWithContact from './model-with-contact.js';
 import isMatch from "lodash-es/isMatch";
 import log from '../../log.js';
 import pick from "lodash-es/pick";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Model } from '@converse/skeletor/src/model.js';
 import { TimeoutError } from '../../shared/errors.js';
-import { _converse, api, converse } from "../../index.js";
-import { debouncedPruneHistory, handleCorrection } from '@converse/headless/shared/chat/utils.js';
+import { debouncedPruneHistory, handleCorrection } from '../../shared/chat/utils.js';
 import { filesize } from "filesize";
-import { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
+import { getMediaURLsMetadata } from '../../shared/parsers.js';
 import { getOpenPromise } from '@converse/openpromise';
-import { initStorage } from '@converse/headless/utils/storage.js';
+import { initStorage } from '../../utils/storage.js';
 import { isUniView, isEmptyMessage } from '../../utils/core.js';
 import { parseMessage } from './parsers.js';
-import { sendMarker } from '@converse/headless/shared/actions.js';
+import { sendMarker } from '../../shared/actions.js';
 
 const { Strophe, $msg } = converse.env;
 

+ 6 - 6
src/headless/plugins/chat/parsers.js

@@ -1,8 +1,9 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import dayjs from 'dayjs';
 import log from '../../log.js';
-import u from '@converse/headless/utils/core';
-import { _converse, api, converse } from '@converse/headless';
-import { rejectMessage } from '@converse/headless/shared/actions';
+import u from '../../utils/core';
+import { rejectMessage } from '../../shared/actions';
 
 import {
     StanzaParseError,
@@ -24,7 +25,7 @@ import {
     isServerMessage,
     isValidReceiptRequest,
     throwErrorIfInvalidForward,
-} from '@converse/headless/shared/parsers';
+} from '../../shared/parsers';
 
 const { Strophe, sizzle } = converse.env;
 
@@ -33,8 +34,7 @@ const { Strophe, sizzle } = converse.env;
  * Parses a passed in message stanza and returns an object of attributes.
  * @method st#parseMessage
  * @param { Element } stanza - The message stanza
- * @param { _converse } _converse
- * @returns { (MessageAttributes|Error) }
+ * @returns { Promise<MessageAttributes|Error> }
  */
 export async function parseMessage (stanza) {
     throwErrorIfInvalidForward(stanza);

+ 5 - 3
src/headless/plugins/chat/utils.js

@@ -1,7 +1,9 @@
-import { _converse, api, converse, log } from '@converse/headless';
-import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import log from '../../log.js';
+import { isArchived, isHeadline, isServerMessage, } from '../../shared/parsers';
 import { parseMessage } from './parsers.js';
-import { shouldClearCache } from '@converse/headless/utils/core.js';
+import { shouldClearCache } from '../../utils/core.js';
 
 const { Strophe, u } = converse.env;
 

+ 2 - 1
src/headless/plugins/chatboxes/api.js

@@ -1,4 +1,5 @@
-import { _converse, api } from "../../index.js";
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
 import { createChatBox } from './utils.js';
 
 /**

+ 3 - 2
src/headless/plugins/chatboxes/chatboxes.js

@@ -1,6 +1,7 @@
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
 import { Collection } from "@converse/skeletor/src/collection";
-import { _converse, api } from "../../index.js";
-import { initStorage } from '@converse/headless/utils/storage.js';
+import { initStorage } from '../../utils/storage.js';
 
 const ChatBoxes = Collection.extend({
     comparator: 'time_opened',

+ 2 - 1
src/headless/plugins/chatboxes/index.js

@@ -4,8 +4,9 @@
  */
 import "../emoji/index.js";
 import ChatBoxes from './chatboxes.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import chatboxes_api from './api.js';
-import { _converse, api, converse } from "../../index.js";
 
 const { Strophe } = converse.env;
 

+ 2 - 1
src/headless/plugins/chatboxes/utils.js

@@ -1,4 +1,5 @@
-import { _converse, converse } from "../../index.js";
+import _converse from '../../shared/_converse.js';
+import { converse } from '../../shared/api/index.js';
 import log from "../../log";
 
 const { Strophe } = converse.env;

+ 2 - 1
src/headless/plugins/disco/api.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
-import { _converse, api, converse } from "@converse/headless";
 import { getOpenPromise } from '@converse/openpromise';
 
 const { Strophe, $iq } = converse.env;

+ 3 - 1
src/headless/plugins/disco/entity.js

@@ -1,7 +1,9 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import log from '../../log.js';
 import sizzle from 'sizzle';
 import { Collection } from '@converse/skeletor/src/collection';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse, api, converse, log } from '@converse/headless';
 import { getOpenPromise } from '@converse/openpromise';
 
 const { Strophe } = converse.env;

+ 3 - 2
src/headless/plugins/disco/index.js

@@ -5,7 +5,9 @@
  */
 import DiscoEntities from './entities.js';
 import DiscoEntity from './entity.js';
-import { _converse, api, converse } from '@converse/headless';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import disco_api from './api.js';
 import {
     clearSession,
     initStreamFeatures,
@@ -13,7 +15,6 @@ import {
     notifyStreamFeaturesAdded,
     populateStreamFeatures
 } from './utils.js';
-import disco_api from './api.js';
 
 const { Strophe } = converse.env;
 

+ 2 - 1
src/headless/plugins/disco/utils.js

@@ -1,4 +1,5 @@
-import { _converse, api, converse } from "@converse/headless";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Collection } from "@converse/skeletor/src/collection";
 
 const { Strophe, $iq } = converse.env;

+ 2 - 1
src/headless/plugins/emoji/index.js

@@ -4,8 +4,9 @@
  * @license Mozilla Public License (MPLv2)
  */
 import './utils.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse, api, converse } from "../../index.js";
 import { getOpenPromise } from '@converse/openpromise';
 
 

+ 1 - 1
src/headless/plugins/emoji/utils.js

@@ -1,5 +1,5 @@
 import { ASCII_REPLACE_REGEX, CODEPOINTS_REGEX } from './regexes.js';
-import { converse } from "../../index.js";
+import { converse } from '../../shared/api/index.js';
 
 const { u } = converse.env;
 

+ 2 - 1
src/headless/plugins/headlines/api.js

@@ -1,4 +1,5 @@
-import { _converse, api } from "@converse/headless";
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
 
 export default {
     /**

+ 2 - 2
src/headless/plugins/headlines/feed.js

@@ -1,6 +1,6 @@
-import ChatBox from '@converse/headless/plugins/chat/model.js';
+import ChatBox from '../../plugins/chat/model.js';
+import _converse from '../../shared/_converse.js';
 import api from "../../shared/api/index.js";
-import { _converse } from '../../index.js';
 
 
 export default class HeadlinesFeed extends ChatBox {

+ 2 - 1
src/headless/plugins/headlines/index.js

@@ -3,8 +3,9 @@
  * @copyright 2022, the Converse.js contributors
  */
 import HeadlinesFeed from './feed.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import headlines_api from './api.js';
-import { _converse, api, converse } from "../../index.js";
 import { onHeadlineMessage } from './utils.js';
 
 

+ 7 - 5
src/headless/plugins/headlines/utils.js

@@ -1,6 +1,8 @@
-import { _converse, api } from "@converse/headless";
-import { isHeadline, isServerMessage } from '@converse/headless/shared/parsers';
-import { parseMessage } from '@converse/headless/plugins/chat/parsers';
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
+import { HEADLINES_TYPE } from '../../shared/constants.js';
+import { isHeadline, isServerMessage } from '../../shared/parsers.js';
+import { parseMessage } from '../../plugins/chat/parsers.js';
 
 /**
  * Handler method for all incoming messages of type "headline".
@@ -23,10 +25,10 @@ export async function onHeadlineMessage (stanza) {
         const chatbox = _converse.chatboxes.create({
             'id': from_jid,
             'jid': from_jid,
-            'type': _converse.HEADLINES_TYPE,
+            'type': HEADLINES_TYPE,
             'from': from_jid
         });
-        const attrs = await parseMessage(stanza, _converse);
+        const attrs = await parseMessage(stanza);
         await chatbox.createMessage(attrs);
         api.trigger('message', {chatbox, stanza, attrs});
     }

+ 5 - 3
src/headless/plugins/mam/api.js

@@ -1,10 +1,12 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import dayjs from 'dayjs';
 import log from '../../log.js';
 import sizzle from "sizzle";
-import { RSM } from '@converse/headless/shared/rsm';
+import { RSM } from '../../shared/rsm';
+import { Strophe, $iq } from 'strophe.js';
 import { TimeoutError } from '../../shared/errors.js';
-import { _converse, api, converse } from "@converse/headless";
 
-const { Strophe, $iq, dayjs } = converse.env;
 const { NS } = Strophe;
 const u = converse.env.utils;
 

+ 3 - 2
src/headless/plugins/mam/index.js

@@ -5,7 +5,10 @@
  */
 import '../disco/index.js';
 import MAMPlaceholderMessage from './placeholder.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import mam_api from './api.js';
+import { Strophe } from 'strophe.js';
 import {
     onMAMError,
     onMAMPreferences,
@@ -14,9 +17,7 @@ import {
     fetchNewestMessages,
     handleMAMResult
 } from './utils.js';
-import { _converse, api, converse } from '@converse/headless';
 
-const { Strophe } = converse.env;
 const { NS } = Strophe;
 
 converse.plugins.add('converse-mam', {

+ 2 - 4
src/headless/plugins/mam/placeholder.js

@@ -1,13 +1,11 @@
 import { Model } from '@converse/skeletor/src/model.js';
-import { converse } from '../../index.js';
-
-const u = converse.env.utils;
+import { getUniqueId } from '../../utils/core.js';
 
 export default class MAMPlaceholderMessage extends Model {
 
     defaults () { // eslint-disable-line class-methods-use-this
         return {
-            'msgid': u.getUniqueId(),
+            'msgid': getUniqueId(),
             'is_ephemeral': false
         };
     }

+ 5 - 4
src/headless/plugins/mam/utils.js

@@ -1,11 +1,12 @@
 import MAMPlaceholderMessage from './placeholder.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from '../../log.js';
 import sizzle from 'sizzle';
-import { _converse, api, converse } from '@converse/headless';
-import { parseMUCMessage } from '@converse/headless/plugins/muc/parsers';
-import { parseMessage } from '@converse/headless/plugins/chat/parsers';
+import { Strophe, $iq } from 'strophe.js';
+import { parseMUCMessage } from '../../plugins/muc/parsers';
+import { parseMessage } from '../../plugins/chat/parsers';
 
-const { Strophe, $iq } = converse.env;
 const { NS } = Strophe;
 const u = converse.env.utils;
 

+ 4 - 2
src/headless/plugins/muc/affiliations/utils.js

@@ -2,8 +2,10 @@
  * @copyright The Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
-import { AFFILIATIONS } from '@converse/headless/plugins/muc/index.js';
-import { _converse, api, converse, log } from '@converse/headless';
+import _converse from '../../../shared/_converse.js';
+import api, { converse } from '../../../shared/api/index.js';
+import log from '../../../log.js';
+import { AFFILIATIONS } from '../constants.js';
 import { parseMemberListIQ } from '../parsers.js';
 
 const { Strophe, $iq, u } = converse.env;

+ 2 - 1
src/headless/plugins/muc/api.js

@@ -1,6 +1,7 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from '../../log';
 import { Strophe } from 'strophe.js';
-import { _converse, api, converse } from '../../index.js';
 
 const { u } = converse.env;
 

+ 2 - 4
src/headless/plugins/muc/index.js

@@ -13,7 +13,8 @@ import ChatRoomOccupants from './occupants.js';
 import affiliations_api from './affiliations/api.js';
 import muc_api from './api.js';
 import { Collection } from '@converse/skeletor/src/collection';
-import { _converse, api, converse } from '../../index.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import {
     autoJoinRooms,
     disconnectChatRooms,
@@ -42,9 +43,6 @@ import {
     ROOM_FEATURES,
 } from './constants.js';
 
-export const ROLES = ['moderator', 'participant', 'visitor'];
-export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
-
 converse.AFFILIATION_CHANGES = AFFILIATION_CHANGES;
 converse.AFFILIATION_CHANGES_LIST = AFFILIATION_CHANGES_LIST;
 converse.MUC_TRAFFIC_STATES =  MUC_TRAFFIC_STATES;

+ 2 - 1
src/headless/plugins/muc/message.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api from '../../shared/api/index.js';
 import { Strophe } from 'strophe.js';
-import { _converse, api } from '../../index.js';
 
 /**
  * Mixing that turns a Message model into a ChatRoomMessage model.

+ 2 - 1
src/headless/plugins/muc/muc.js

@@ -1,3 +1,5 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import debounce from 'lodash-es/debounce';
 import log from '../../log';
 import p from '../../utils/parse-helpers';
@@ -7,7 +9,6 @@ import { Model } from '@converse/skeletor/src/model.js';
 import { ROOMSTATUS } from './constants.js';
 import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js';
 import { TimeoutError } from '../../shared/errors.js';
-import { _converse, api, converse } from '../../index.js';
 import { computeAffiliationsDelta, setAffiliations, getAffiliationList }  from './affiliations/utils.js';
 import { getOpenPromise } from '@converse/openpromise';
 import { handleCorrection } from '../../shared/chat/utils.js';

+ 3 - 2
src/headless/plugins/muc/occupants.js

@@ -1,12 +1,13 @@
 import ChatRoomOccupant from './occupant.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Collection } from '@converse/skeletor/src/collection.js';
 import { MUC_ROLE_WEIGHTS } from './constants.js';
 import { Model } from '@converse/skeletor/src/model.js';
 import { Strophe } from 'strophe.js';
-import { _converse, api, converse } from '../../index.js';
 import { getAffiliationList } from './affiliations/utils.js';
 import { getAutoFetchedAffiliationLists } from './utils.js';
-import { getUniqueId } from '@converse/headless/utils/core.js';
+import { getUniqueId } from '../../utils/core.js';
 
 const { u } = converse.env;
 

+ 3 - 2
src/headless/plugins/muc/parsers.js

@@ -1,4 +1,6 @@
 import dayjs from 'dayjs';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import {
     StanzaParseError,
     getChatMarker,
@@ -19,8 +21,7 @@ import {
     isHeadline,
     isValidReceiptRequest,
     throwErrorIfInvalidForward,
-} from '@converse/headless/shared/parsers';
-import { _converse, api, converse } from '@converse/headless';
+} from '../../shared/parsers';
 
 const { Strophe, sizzle, u } = converse.env;
 const { NS } = Strophe;

+ 4 - 2
src/headless/plugins/muc/utils.js

@@ -1,6 +1,8 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import log from '../../log.js';
 import { ROLES } from './constants.js';
-import { _converse, api, converse, log } from '@converse/headless';
-import { safeSave } from '@converse/headless/utils/core.js';
+import { safeSave } from '../../utils/core.js';
 
 const { Strophe, sizzle, u } = converse.env;
 

+ 2 - 1
src/headless/plugins/ping/api.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from '../../log.js';
-import { _converse, api, converse } from "@converse/headless";
 import { setLastStanzaDate } from './utils.js';
 
 const { Strophe, $iq, u } = converse.env;

+ 1 - 1
src/headless/plugins/ping/index.js

@@ -5,8 +5,8 @@
  * @copyright 2022, the Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
+import api, { converse } from '../../shared/api/index.js';
 import ping_api from './api.js';
-import { api, converse } from "@converse/headless";
 import { onWindowStateChanged, registerHandlers, unregisterIntervalHandler } from './utils.js';
 
 const { Strophe } = converse.env;

+ 2 - 1
src/headless/plugins/ping/utils.js

@@ -1,4 +1,5 @@
-import { _converse, api, converse } from "@converse/headless";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 
 const { Strophe, $iq } = converse.env;
 

+ 2 - 1
src/headless/plugins/pubsub.js

@@ -4,7 +4,8 @@
  * @license Mozilla Public License (MPLv2)
  */
 import "./disco/index.js";
-import { _converse, api, converse } from "../index.js";
+import _converse from '../shared/_converse.js';
+import api, { converse } from '../shared/api/index.js';
 import log from "../log.js";
 
 const { Strophe, $iq } = converse.env;

+ 2 - 1
src/headless/plugins/roster/api.js

@@ -1,4 +1,5 @@
-import { _converse, api, converse } from "@converse/headless";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 
 const { Strophe } = converse.env;
 

+ 3 - 2
src/headless/plugins/roster/contact.js

@@ -1,6 +1,7 @@
-import '@converse/headless/plugins/status/api.js';
+import '../../plugins/status/api.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse, api, converse } from "@converse/headless";
 import { getOpenPromise } from '@converse/openpromise';
 import { rejectPresenceSubscription } from './utils.js';
 

+ 3 - 2
src/headless/plugins/roster/contacts.js

@@ -1,9 +1,10 @@
 import RosterContact from './contact.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
 import { Collection } from "@converse/skeletor/src/collection";
 import { Model } from "@converse/skeletor/src/model";
-import { _converse, api, converse } from "@converse/headless";
-import { initStorage } from '@converse/headless/utils/storage.js';
+import { initStorage } from '../../utils/storage.js';
 import { rejectPresenceSubscription } from './utils.js';
 
 const { Strophe, $iq, sizzle, u } = converse.env;

+ 3 - 2
src/headless/plugins/roster/index.js

@@ -2,12 +2,13 @@
  * @copyright The Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
-import '@converse/headless/plugins/status';
+import '../../plugins/status/index.js';
 import RosterContact from './contact.js';
 import RosterContacts from './contacts.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import roster_api from './api.js';
 import { Presence, Presences } from './presence.js';
-import { _converse, api, converse } from '@converse/headless';
 import {
     onChatBoxesInitialized,
     onClearSession,

+ 2 - 2
src/headless/plugins/roster/presence.js

@@ -1,7 +1,7 @@
 import { Collection } from "@converse/skeletor/src/collection";
 import { Model } from '@converse/skeletor/src/model.js';
-import { converse } from "@converse/headless";
-import { initStorage } from '@converse/headless/utils/storage.js';
+import { converse } from '../../shared/api/index.js';
+import { initStorage } from '../../utils/storage.js';
 
 const { Strophe, dayjs, sizzle } = converse.env;
 

+ 5 - 4
src/headless/plugins/roster/utils.js

@@ -1,10 +1,11 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
 import { Model } from '@converse/skeletor/src/model.js';
-import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
+import { RosterFilter } from '../../plugins/roster/filter.js';
 import { STATUS_WEIGHTS } from "../../shared/constants";
-import { _converse, api, converse } from "@converse/headless";
-import { initStorage } from '@converse/headless/utils/storage.js';
-import { shouldClearCache } from '@converse/headless/utils/core.js';
+import { initStorage } from '../../utils/storage.js';
+import { shouldClearCache } from '../../utils/core.js';
 
 const { $pres } = converse.env;
 

+ 1 - 1
src/headless/plugins/smacks/index.js

@@ -3,7 +3,7 @@
  * @license Mozilla Public License (MPLv2)
  * @description Converse.js plugin which adds support for XEP-0198: Stream Management
  */
-import { api, converse } from '@converse/headless';
+import api, { converse } from '../../shared/api/index.js';
 import { enableStreamManagement, initSessionData, sendEnableStanza, onStanzaSent } from './utils.js';
 
 const { Strophe } = converse.env;

+ 2 - 1
src/headless/plugins/smacks/utils.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from '../../log.js';
-import { _converse, api, converse } from '@converse/headless';
 import { getOpenPromise } from '@converse/openpromise';
 
 const { Strophe } = converse.env;

+ 3 - 2
src/headless/plugins/status/index.js

@@ -3,9 +3,10 @@
  * @license Mozilla Public License (MPLv2)
  */
 import XMPPStatus from './status.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import status_api from './api.js';
-import { _converse, api, converse } from '@converse/headless';
-import { shouldClearCache } from '@converse/headless/utils/core.js';
+import { shouldClearCache } from '../../utils/core.js';
 import {
     addStatusToMUCJoinPresence,
     initStatus,

+ 2 - 1
src/headless/plugins/status/status.js

@@ -1,5 +1,6 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse, api, converse } from '@converse/headless';
 
 const { Strophe, $pres } = converse.env;
 

+ 3 - 2
src/headless/plugins/status/utils.js

@@ -1,5 +1,6 @@
-import { _converse, api, converse } from '@converse/headless';
-import { initStorage } from '@converse/headless/utils/storage.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
+import { initStorage } from '../../utils/storage.js';
 
 const { Strophe, $build } = converse.env;
 

+ 2 - 1
src/headless/plugins/vcard/api.js

@@ -1,5 +1,6 @@
 import log from "../../log.js";
-import { _converse, api, converse } from "../../index.js";
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import { createStanza, getVCard } from './utils.js';
 
 const { dayjs, u } = converse.env;

+ 3 - 2
src/headless/plugins/vcard/index.js

@@ -2,11 +2,12 @@
  * @copyright The Converse.js contributors
  * @license Mozilla Public License (MPLv2)
  */
-import "../status";
+import "../status/index.js";
 import VCard from './vcard.js';
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import vcard_api from './api.js';
 import { Collection } from "@converse/skeletor/src/collection";
-import { _converse, api, converse } from "../../index.js";
 import {
     clearVCardsSession,
     initVCardCollection,

+ 4 - 3
src/headless/plugins/vcard/utils.js

@@ -1,7 +1,8 @@
+import _converse from '../../shared/_converse.js';
+import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
-import { _converse, api, converse } from "../../index.js";
-import { initStorage } from '@converse/headless/utils/storage.js';
-import { shouldClearCache } from '@converse/headless/utils/core.js';
+import { initStorage } from '../../utils/storage.js';
+import { shouldClearCache } from '../../utils/core.js';
 
 const { Strophe, $iq, u } = converse.env;
 

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

@@ -1,5 +1,5 @@
+import _converse from '../../shared/_converse.js';
 import { Model } from '@converse/skeletor/src/model.js';
-import { _converse } from "../../index.js";
 
 /**
  * Represents a VCard

+ 2 - 1
src/headless/shared/actions.js

@@ -1,6 +1,7 @@
 import log from '../log';
 import { Strophe, $msg } from 'strophe.js';
-import { _converse, api, converse } from '@converse/headless';
+import _converse from './_converse.js';
+import api, { converse } from './api/index.js';
 
 const u = converse.env.utils;
 

+ 2 - 0
src/headless/shared/api/index.js

@@ -28,3 +28,5 @@ const api = {
 };
 
 export default api;
+
+export { converse } from './public.js';

+ 2 - 2
src/headless/shared/api/presence.js

@@ -1,5 +1,5 @@
-import { _converse, api } from '../../index.js';
-
+import _converse from '../_converse.js';
+import api from '../../shared/api/index.js';
 
 export default {
     /**

+ 1 - 1
src/headless/shared/chat/utils.js

@@ -1,5 +1,5 @@
 import debounce from 'lodash-es/debounce.js';
-import { api, converse } from '@converse/headless';
+import api, { converse } from '../../shared/api/index.js';
 
 const { u } = converse.env;
 

+ 2 - 1
src/headless/shared/connection/index.js

@@ -1,10 +1,11 @@
 import debounce from 'lodash-es/debounce';
 import log from "../../log.js";
 import sizzle from 'sizzle';
+import _converse from '../_converse.js';
+import api from '../api/index.js';
 import { ANONYMOUS, BOSH_WAIT, LOGOUT } from '../../shared/constants.js';
 import { CONNECTION_STATUS } from '../constants';
 import { Strophe } from 'strophe.js';
-import { _converse, api } from "../../index.js";
 import { clearSession, tearDown } from "../../utils/core.js";
 import { getOpenPromise } from '@converse/openpromise';
 import { setUserJID, } from '../../utils/init.js';

+ 7 - 6
src/headless/shared/parsers.js

@@ -1,18 +1,19 @@
 import URI from 'urijs';
+import _converse from './_converse.js';
+import api from './api/index.js';
 import dayjs from 'dayjs';
-import log from '@converse/headless/log';
+import log from '../log.js';
 import sizzle from 'sizzle';
 import { Strophe } from 'strophe.js';
-import { URL_PARSE_OPTIONS } from '@converse/headless/shared/constants.js';
-import { _converse, api } from '@converse/headless';
-import { decodeHTMLEntities } from '@converse/headless/utils/core.js';
-import { rejectMessage } from '@converse/headless/shared/actions';
+import { URL_PARSE_OPTIONS } from './constants.js';
+import { decodeHTMLEntities } from '..//utils/core.js';
+import { rejectMessage } from './actions';
 import {
     isAudioURL,
     isEncryptedFileURL,
     isImageURL,
     isVideoURL
-} from '@converse/headless/utils/url.js';
+} from '../utils/url.js';
 
 const { NS } = Strophe;
 

+ 2 - 1
src/headless/shared/rsm.js

@@ -6,7 +6,8 @@
  *   Some code taken from the Strophe RSM plugin, licensed under the MIT License
  *   Copyright 2006-2017 Strophe (https://github.com/strophe/strophejs)
  */
-import { _converse, converse } from "../index.js";
+import _converse from './_converse.js';
+import { converse } from './api/index.js';
 import pick from 'lodash-es/pick';
 
 const { Strophe, $build } = converse.env;

+ 2 - 2
src/headless/shared/settings/utils.js

@@ -1,8 +1,8 @@
 import _converse from '../_converse.js';
 import isEqual from "lodash-es/isEqual.js";
-import log from '@converse/headless/log';
+import log from '../../log.js';
 import pick from 'lodash-es/pick';
-import u from '@converse/headless/utils/core';
+import u from '../../utils/core';
 import { DEFAULT_SETTINGS } from './constants.js';
 import { Events } from '@converse/skeletor/src/events.js';
 import { Model } from '@converse/skeletor/src/model.js';

+ 18 - 105
src/headless/utils/core.js

@@ -4,7 +4,7 @@
  * @description This is the core utilities module.
  */
 import DOMPurify from 'dompurify';
-import _converse from '@converse/headless/shared/_converse.js';
+import _converse from '../shared/_converse.js';
 import log from '../log.js';
 import sizzle from "sizzle";
 import { Model } from '@converse/skeletor/src/model.js';
@@ -12,7 +12,21 @@ import { Strophe } from 'strophe.js';
 import { getOpenPromise } from '@converse/openpromise';
 import { settings_api } from '../shared/settings/api.js';
 import { stx , toStanza } from './stanza.js';
-import { getSelectValues, webForm2xForm } from './form.js';
+import {
+    getCurrentWord,
+    getSelectValues,
+    isMentionBoundary,
+    placeCaretAtEnd,
+    replaceCurrentWord,
+    webForm2xForm
+} from './form.js';
+import {
+    getOuterWidth,
+    isElement,
+    isTagEqual,
+    queryChildren,
+    stringToElement,
+} from './html.js';
 import {
     arrayBufferToHex,
     arrayBufferToString,
@@ -55,10 +69,6 @@ const u = {
     webForm2xForm,
 };
 
-export function isElement (el) {
-    return el instanceof Element || el instanceof HTMLDocument;
-}
-
 export function isError (obj) {
     return Object.prototype.toString.call(obj) === "[object Error]";
 }
@@ -138,19 +148,6 @@ export function prefixMentions (message) {
     return text;
 }
 
-function isTagEqual (stanza, name) {
-    if (stanza.tree?.()) {
-        return isTagEqual(stanza.tree(), name);
-    } else if (!(stanza instanceof Element)) {
-        throw Error(
-            "isTagEqual called with value which isn't "+
-            "an element or Strophe.Builder instance");
-    } else {
-        return Strophe.isTagEqual(stanza, name);
-    }
-}
-
-
 u.getJIDFromURI = function (jid) {
     return jid.startsWith('xmpp:') && jid.endsWith('?join')
         ? jid.replace(/^xmpp:/, '').replace(/\?join$/, '')
@@ -272,43 +269,6 @@ export function merge (dst, src) {
     }
 }
 
-/**
- * @param {HTMLElement} el
- * @param {boolean} include_margin
- */
-function getOuterWidth (el, include_margin=false) {
-    let width = el.offsetWidth;
-    if (!include_margin) {
-        return width;
-    }
-    const style = window.getComputedStyle(el);
-    width += parseInt(style.marginLeft ? style.marginLeft : '0', 10) +
-             parseInt(style.marginRight ? style.marginRight : '0', 10);
-    return width;
-}
-
-/**
- * Converts an HTML string into a DOM element.
- * Expects that the HTML string has only one top-level element,
- * i.e. not multiple ones.
- * @method u#stringToElement
- * @param {string} s - The HTML string
- */
-function stringToElement (s) {
-    var div = document.createElement('div');
-    div.innerHTML = s;
-    return div.firstElementChild;
-}
-
-/**
- * Returns a list of children of the DOM element that match the selector.
- * @method u#queryChildren
- * @param {HTMLElement} el - the DOM element
- * @param {string} selector - the selector they should be matched against
- */
-function queryChildren (el, selector) {
-    return Array.from(el.childNodes).filter(el => (el instanceof Element) && el.matches(selector));
-}
 
 u.contains = function (attr, query) {
     const checker = (item, key) => item.get(key).toLowerCase().includes(query.toLowerCase());
@@ -378,39 +338,6 @@ u.siblingIndex = function (el) {
     return i;
 };
 
-/**
- * Returns the current word being written in the input element
- * @method u#getCurrentWord
- * @param {HTMLInputElement} input - The HTMLElement in which text is being entered
- * @param {number} [index] - An optional rightmost boundary index. If given, the text
- *  value of the input element will only be considered up until this index.
- * @param {string} [delineator] - An optional string delineator to
- *  differentiate between words.
- * @private
- */
-function getCurrentWord (input, index, delineator) {
-    if (!index) {
-        index = input.selectionEnd || undefined;
-    }
-    let [word] = input.value.slice(0, index).split(/\s/).slice(-1);
-    if (delineator) {
-        [word] = word.split(delineator).slice(-1);
-    }
-    return word;
-}
-
-u.isMentionBoundary = (s) => s !== '@' && RegExp(`(\\p{Z}|\\p{P})`, 'u').test(s);
-
-u.replaceCurrentWord = function (input, new_value) {
-    const caret = input.selectionEnd || undefined;
-    const current_word = input.value.slice(0, caret).split(/\s/).pop();
-    const value = input.value;
-    const mention_boundary = u.isMentionBoundary(current_word[0]) ? current_word[0] : '';
-    input.value = value.slice(0, caret - current_word.length) + mention_boundary + `${new_value} ` + value.slice(caret);
-    const selection_end = caret - current_word.length + new_value.length + 1;
-    input.selectionEnd = mention_boundary ? selection_end + 1 : selection_end;
-};
-
 /**
  * @param {Element} el
  * @param {string} name
@@ -428,22 +355,6 @@ export function getRandomInt (max) {
     return (Math.random() * max) | 0;
 }
 
-/**
- * @param {HTMLTextAreaElement} textarea
- */
-function placeCaretAtEnd (textarea) {
-    if (textarea !== document.activeElement) {
-        textarea.focus();
-    }
-    // Double the length because Opera is inconsistent about whether a carriage return is one character or two.
-    const len = textarea.value.length * 2;
-    // Timeout seems to be required for Blink
-    setTimeout(() => textarea.setSelectionRange(len, len), 1);
-    // Scroll to the bottom, in case we're in a tall textarea
-    // (Necessary for Firefox and Chrome)
-    textarea.scrollTop = 999999;
-}
-
 /**
  * @param {string} [suffix]
  * @return {string}
@@ -611,6 +522,7 @@ export default Object.assign({
     getCurrentWord,
     getOuterWidth,
     getRandomInt,
+    isMentionBoundary,
     getUniqueId,
     isElement,
     isEmptyMessage,
@@ -621,6 +533,7 @@ export default Object.assign({
     placeCaretAtEnd,
     prefixMentions,
     queryChildren,
+    replaceCurrentWord,
     safeSave,
     saveWindowState,
     shouldClearCache,

+ 57 - 0
src/headless/utils/form.js

@@ -52,3 +52,60 @@ export function webForm2xForm (field) {
     }
     return toStanza(tplXformField(name, Array.isArray(value) ? value.map(tplXformValue) : tplXformValue(value)));
 }
+
+/**
+ * Returns the current word being written in the input element
+ * @method u#getCurrentWord
+ * @param {HTMLInputElement} input - The HTMLElement in which text is being entered
+ * @param {number} [index] - An optional rightmost boundary index. If given, the text
+ *  value of the input element will only be considered up until this index.
+ * @param {string} [delineator] - An optional string delineator to
+ *  differentiate between words.
+ */
+export function getCurrentWord (input, index, delineator) {
+    if (!index) {
+        index = input.selectionEnd || undefined;
+    }
+    let [word] = input.value.slice(0, index).split(/\s/).slice(-1);
+    if (delineator) {
+        [word] = word.split(delineator).slice(-1);
+    }
+    return word;
+}
+
+/**
+ * @param {string} s
+ */
+export function isMentionBoundary (s) {
+    return s !== '@' && RegExp(`(\\p{Z}|\\p{P})`, 'u').test(s);
+}
+
+/**
+ * @param {HTMLInputElement} input - The HTMLElement in which text is being entered
+ * @param {string} new_value
+ */
+export function replaceCurrentWord (input, new_value) {
+    const caret = input.selectionEnd || undefined;
+    const current_word = input.value.slice(0, caret).split(/\s/).pop();
+    const value = input.value;
+    const mention_boundary = isMentionBoundary(current_word[0]) ? current_word[0] : '';
+    input.value = value.slice(0, caret - current_word.length) + mention_boundary + `${new_value} ` + value.slice(caret);
+    const selection_end = caret - current_word.length + new_value.length + 1;
+    input.selectionEnd = mention_boundary ? selection_end + 1 : selection_end;
+}
+
+/**
+ * @param {HTMLTextAreaElement} textarea
+ */
+export function placeCaretAtEnd (textarea) {
+    if (textarea !== document.activeElement) {
+        textarea.focus();
+    }
+    // Double the length because Opera is inconsistent about whether a carriage return is one character or two.
+    const len = textarea.value.length * 2;
+    // Timeout seems to be required for Blink
+    setTimeout(() => textarea.setSelectionRange(len, len), 1);
+    // Scroll to the bottom, in case we're in a tall textarea
+    // (Necessary for Firefox and Chrome)
+    textarea.scrollTop = 999999;
+}

+ 2 - 1
src/headless/utils/url.js

@@ -1,5 +1,6 @@
 import URI from 'urijs';
-import { api, log } from '@converse/headless';
+import log from '../log.js';
+import api from '../shared/api/index.js';
 
 /**
  * Given a url, check whether the protocol being used is allowed for rendering

+ 1 - 1
src/shared/directives/styling.js

@@ -1,6 +1,6 @@
 import { log } from '@converse/headless';
 import { Directive, directive } from 'lit/directive.js';
-import { RichText } from 'shared/rich-text.js';
+import { RichText } from '../rich-text.js';
 import { html } from 'lit';
 import { until } from 'lit/directives/until.js';
 

+ 1 - 1
src/shared/styling.js

@@ -5,7 +5,7 @@
  * @todo Other parsing helpers can be made more abstract and placed here.
  */
 import { html } from 'lit';
-import { renderStylingDirectiveBody } from 'shared/directives/styling.js';
+import { renderStylingDirectiveBody } from './directives/styling.js';
 
 
 const bracketing_directives = ['*', '_', '~', '`'];

+ 7 - 0
webpack/webpack.build.js

@@ -1,5 +1,6 @@
 /* global __dirname, module, process */
 const ASSET_PATH = process.env.ASSET_PATH || '/dist/'; // eslint-disable-line no-process-env
+const CircularDependencyPlugin = require('circular-dependency-plugin');
 const CopyWebpackPlugin = require('copy-webpack-plugin');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const common = require("./webpack.common.js");
@@ -27,6 +28,12 @@ const plugins = [
     }),
     new webpack.DefinePlugin({ // This makes it possible for us to safely use env vars on our code
         'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
+    }),
+    new CircularDependencyPlugin({
+      exclude: /node_modules/,
+      failOnError: false,
+      allowAsyncCycles: false,
+      cwd: process.cwd(),
     })
 ];