serverless-ircd 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/.github/workflows/ci.yml +6 -1
  2. package/.gitmodules +3 -0
  3. package/CHANGELOG.md +214 -0
  4. package/README.md +110 -23
  5. package/apps/aws-stack/package.json +1 -1
  6. package/apps/cf-tcp-container/package.json +1 -1
  7. package/apps/cf-worker/package.json +1 -1
  8. package/apps/cf-worker/src/origin-allowlist.ts +99 -0
  9. package/apps/cf-worker/src/worker.ts +89 -9
  10. package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
  11. package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
  12. package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
  13. package/apps/cf-worker/tests/smoke.test.ts +28 -2
  14. package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
  15. package/apps/cf-worker/wrangler.test.toml +23 -0
  16. package/apps/cf-worker/wrangler.toml +54 -0
  17. package/apps/local-cli/package.json +1 -1
  18. package/apps/web/landing/index.html +217 -0
  19. package/apps/web/package.json +28 -0
  20. package/apps/web/scripts/build.mjs +125 -0
  21. package/apps/web/src/build-env.ts +43 -0
  22. package/apps/web/src/config-schema.ts +138 -0
  23. package/apps/web/static/config.json +28 -0
  24. package/apps/web/static/config.prod.json +28 -0
  25. package/apps/web/static/config.staging.json +28 -0
  26. package/apps/web/tests/build-env.test.ts +63 -0
  27. package/apps/web/tests/build-smoke.test.ts +103 -0
  28. package/apps/web/tests/config-schema.test.ts +432 -0
  29. package/apps/web/tests/workspace.test.ts +12 -0
  30. package/apps/web/tsconfig.json +10 -0
  31. package/apps/web/upstream/.browserslistrc +3 -0
  32. package/apps/web/upstream/.editorconfig +9 -0
  33. package/apps/web/upstream/.eslintignore +3 -0
  34. package/apps/web/upstream/.eslintrc.js +90 -0
  35. package/apps/web/upstream/.github/FUNDING.yml +12 -0
  36. package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  37. package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  38. package/apps/web/upstream/.github/workflows/push.yml +26 -0
  39. package/apps/web/upstream/.github/workflows/release.yml +33 -0
  40. package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
  41. package/apps/web/upstream/.prettierrc.js +9 -0
  42. package/apps/web/upstream/.stylelintignore +5 -0
  43. package/apps/web/upstream/.stylelintrc.js +21 -0
  44. package/apps/web/upstream/LICENSE +201 -0
  45. package/apps/web/upstream/README.md +76 -0
  46. package/apps/web/upstream/babel.config.js +25 -0
  47. package/apps/web/upstream/crowdin.yml +3 -0
  48. package/apps/web/upstream/index.html +35 -0
  49. package/apps/web/upstream/jest.config.js +7 -0
  50. package/apps/web/upstream/jsdoc.json +22 -0
  51. package/apps/web/upstream/package.json +80 -0
  52. package/apps/web/upstream/scripts/pre-commit +43 -0
  53. package/apps/web/upstream/src/components/App.vue +516 -0
  54. package/apps/web/upstream/src/components/AppSettings.vue +587 -0
  55. package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
  56. package/apps/web/upstream/src/components/Avatar.vue +105 -0
  57. package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
  58. package/apps/web/upstream/src/components/BufferKey.vue +121 -0
  59. package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
  60. package/apps/web/upstream/src/components/Captcha.vue +85 -0
  61. package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
  62. package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
  63. package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
  64. package/apps/web/upstream/src/components/ChannelList.vue +345 -0
  65. package/apps/web/upstream/src/components/Container.vue +401 -0
  66. package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
  67. package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
  68. package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
  69. package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
  70. package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
  71. package/apps/web/upstream/src/components/MessageList.vue +1028 -0
  72. package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
  73. package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
  74. package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
  75. package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
  76. package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
  77. package/apps/web/upstream/src/components/Nicklist.vue +309 -0
  78. package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
  79. package/apps/web/upstream/src/components/NotConnected.vue +271 -0
  80. package/apps/web/upstream/src/components/SelfUser.vue +283 -0
  81. package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
  82. package/apps/web/upstream/src/components/ServerView.vue +126 -0
  83. package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
  84. package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
  85. package/apps/web/upstream/src/components/Sidebar.vue +429 -0
  86. package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
  87. package/apps/web/upstream/src/components/SidebarState.vue +145 -0
  88. package/apps/web/upstream/src/components/StartupError.vue +35 -0
  89. package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
  90. package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
  91. package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
  92. package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
  93. package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
  94. package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
  95. package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
  96. package/apps/web/upstream/src/components/UserBox.vue +728 -0
  97. package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
  98. package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
  99. package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
  100. package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
  101. package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
  102. package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
  103. package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
  104. package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
  105. package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
  106. package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
  107. package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
  108. package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
  109. package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
  110. package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
  111. package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
  112. package/apps/web/upstream/src/helpers/Colours.js +128 -0
  113. package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
  114. package/apps/web/upstream/src/helpers/Md5.js +193 -0
  115. package/apps/web/upstream/src/helpers/Misc.js +401 -0
  116. package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
  117. package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
  118. package/apps/web/upstream/src/libs/AudioManager.js +60 -0
  119. package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
  120. package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
  121. package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
  122. package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
  123. package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
  124. package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
  125. package/apps/web/upstream/src/libs/IPC.js +51 -0
  126. package/apps/web/upstream/src/libs/InputHandler.js +921 -0
  127. package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
  128. package/apps/web/upstream/src/libs/Logger.js +71 -0
  129. package/apps/web/upstream/src/libs/Message.js +169 -0
  130. package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
  131. package/apps/web/upstream/src/libs/MessageParser.js +249 -0
  132. package/apps/web/upstream/src/libs/Notifications.js +78 -0
  133. package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
  134. package/apps/web/upstream/src/libs/ServerSession.js +102 -0
  135. package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
  136. package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
  137. package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
  138. package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
  139. package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
  140. package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
  141. package/apps/web/upstream/src/libs/bufferTools.js +177 -0
  142. package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
  143. package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
  144. package/apps/web/upstream/src/libs/settingTools.js +31 -0
  145. package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
  146. package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
  147. package/apps/web/upstream/src/libs/state/UserState.js +89 -0
  148. package/apps/web/upstream/src/libs/state/common.js +26 -0
  149. package/apps/web/upstream/src/libs/state.js +961 -0
  150. package/apps/web/upstream/src/libs/storage/Local.js +51 -0
  151. package/apps/web/upstream/src/main.js +566 -0
  152. package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
  153. package/apps/web/upstream/src/res/configTemplates.js +373 -0
  154. package/apps/web/upstream/src/res/globalStyle.css +277 -0
  155. package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
  156. package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
  157. package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
  158. package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
  159. package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
  160. package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
  161. package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
  162. package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
  163. package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
  164. package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
  165. package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
  166. package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
  167. package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
  168. package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
  169. package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
  170. package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
  171. package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
  172. package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
  173. package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
  174. package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
  175. package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
  176. package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
  177. package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
  178. package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
  179. package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
  180. package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
  181. package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
  182. package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
  183. package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
  184. package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
  185. package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
  186. package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
  187. package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
  188. package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
  189. package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
  190. package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
  191. package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
  192. package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
  193. package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
  194. package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
  195. package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
  196. package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
  197. package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
  198. package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
  199. package/apps/web/upstream/src/res/localesList.json +27 -0
  200. package/apps/web/upstream/src/res/logo.png +0 -0
  201. package/apps/web/upstream/src/thirdparty/about.html +43 -0
  202. package/apps/web/upstream/src/thirdparty/index.js +5 -0
  203. package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
  204. package/apps/web/upstream/static/config.json +28 -0
  205. package/apps/web/upstream/static/emoticons/smile.png +0 -0
  206. package/apps/web/upstream/static/favicon.png +0 -0
  207. package/apps/web/upstream/static/highlight.mp3 +0 -0
  208. package/apps/web/upstream/static/highlight.ogg +0 -0
  209. package/apps/web/upstream/static/locales/.gitignore +2 -0
  210. package/apps/web/upstream/static/plugins/customise.html.example +10 -0
  211. package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
  212. package/apps/web/upstream/static/themes/common/base.css +1064 -0
  213. package/apps/web/upstream/static/themes/dark/theme.css +196 -0
  214. package/apps/web/upstream/static/themes/default/theme.css +92 -0
  215. package/apps/web/upstream/static/themes/elite/theme.css +248 -0
  216. package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
  217. package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
  218. package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
  219. package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
  220. package/apps/web/upstream/static/themes/sky/theme.css +107 -0
  221. package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
  222. package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
  223. package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
  224. package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
  225. package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
  226. package/apps/web/upstream/vue.config.js +150 -0
  227. package/apps/web/upstream/yarn.lock +9767 -0
  228. package/apps/web/vitest.config.ts +13 -0
  229. package/biome.json +1 -0
  230. package/docs/Home.md +1 -0
  231. package/docs/WebClientGuide.md +535 -0
  232. package/package.json +2 -2
  233. package/packages/aws-adapter/package.json +1 -1
  234. package/packages/cf-adapter/package.json +1 -1
  235. package/packages/cf-adapter/tests/cf-harness.ts +12 -1
  236. package/packages/in-memory-runtime/package.json +1 -1
  237. package/packages/irc-core/package.json +1 -1
  238. package/packages/irc-core/reports/mutation/mutation.html +342 -0
  239. package/packages/irc-core/tests/batch.test.ts +15 -0
  240. package/packages/irc-core/tests/numerics.test.ts +13 -0
  241. package/packages/irc-core/tests/parser.test.ts +109 -0
  242. package/packages/irc-server/package.json +1 -1
  243. package/packages/irc-test-support/package.json +1 -1
  244. package/packages/irc-test-support/src/scenarios.ts +24 -2
  245. package/tools/ci-hardening/package.json +1 -1
  246. package/tools/load-test/package.json +1 -1
  247. package/tools/tcp-ws-forwarder/package.json +1 -1
@@ -0,0 +1,401 @@
1
+ 'kiwi public';
2
+
3
+ /** @module */
4
+
5
+ import _ from 'lodash';
6
+ import Vue from 'vue';
7
+ import strftime from 'strftime';
8
+ import PluginWrapper from '@/components/utils/PluginWrapper';
9
+ import * as TextFormatting from '@/helpers/TextFormatting';
10
+ import { urlRegex } from './TextFormatting';
11
+
12
+ const strftimeUTC = strftime.timezone('+0');
13
+ /**
14
+ * Extract an array of buffers from a string, parsing multiple buffer names and channel keys
15
+ * "#chan,#chan2" => 2 channels without a key
16
+ * "#chan,#chan2 key" => 2 channels, the first having a key
17
+ * "#chan,#chan2 key1,key2" => 2 channels, both having a key
18
+ * "#chan,#chan2 ,key2" => 2 channels, the second having a key
19
+ * @param {string} str List of buffer names and channel keys
20
+ */
21
+ export function extractBuffers(str) {
22
+ let spaceIdx = str.indexOf(' ');
23
+ if (spaceIdx === -1) spaceIdx = str.length;
24
+
25
+ let bufferNames = str.substr(0, spaceIdx).split(',');
26
+ let keys = str.substr(spaceIdx + 1).split(',');
27
+
28
+ let buffers = [];
29
+ bufferNames.forEach((bufferName, idx) => {
30
+ // return if bufferName is empty
31
+ if (!bufferName.trim()) {
32
+ return;
33
+ }
34
+ buffers.push({
35
+ name: bufferName,
36
+ key: keys[idx] || '',
37
+ });
38
+ });
39
+
40
+ return buffers;
41
+ }
42
+
43
+ export function extractURL(str) {
44
+ let matches = str.match(urlRegex);
45
+ return matches ? matches[0] : '';
46
+ }
47
+
48
+ export function stripStyles(str) {
49
+ return str.replace(/(\x03[0-9]{0,2})?([\x02\x16\x1d\x1f]+)?/g, '');
50
+ }
51
+
52
+ /**
53
+ * Does a string mention a nickname?
54
+ * @param {string} input The string to search within
55
+ * @param {string} nick The nick to search for
56
+ */
57
+ export function mentionsNick(input, nick) {
58
+ if (input.toLowerCase().indexOf(nick.toLowerCase()) === -1) {
59
+ return false;
60
+ }
61
+ let punc = '\\s,.!:;+()\\[\\]?¿\\/<>@-';
62
+ let escapedNick = _.escapeRegExp(nick);
63
+ let stylesStrippedInput = stripStyles(input);
64
+ let r = new RegExp(`(^|[${punc}])${escapedNick}([${punc}]|$)`, 'i');
65
+ return r.test(stylesStrippedInput);
66
+ }
67
+
68
+ /**
69
+ * Get a query string value from the current URL
70
+ * @param {string} _name The query string variable name
71
+ * @param {string} _url The full URL to extract the variable from
72
+ */
73
+ export function queryStringVal(_name, _url) {
74
+ let url = _url || window.location.href;
75
+ let name = _.escapeRegExp(_name);
76
+ let regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
77
+ let results = regex.exec(url);
78
+
79
+ if (!results) {
80
+ return null;
81
+ }
82
+ if (!results[2]) {
83
+ return '';
84
+ }
85
+
86
+ return decodeURIComponent(results[2].replace(/\+/g, ' '));
87
+ }
88
+
89
+ /**
90
+ * Convert a known error code to a human readable message
91
+ * @param {string} err The error message from the network connection
92
+ */
93
+ export function networkErrorMessage(err) {
94
+ let errs = {
95
+ err_unknown_host: 'Unknown domain name or host',
96
+ err_forbidden: 'Forbidden to connect',
97
+ err_timeout: 'Took too long to connect',
98
+ err_refused: 'The server refused the connection',
99
+ err_tls: 'Could not connect securely',
100
+ err_proxy: 'The Kiwi IRC server had an error',
101
+ };
102
+
103
+ return errs[err] || 'Unknown error';
104
+ }
105
+
106
+ /**
107
+ * Take a users connection object (usually from startupOptions) and normalise a connection
108
+ * settings object. Parses websocket/direct/kiwiServer/etc options and creates a single
109
+ * object that Kiwi can consistently read from.
110
+ * @param {Object} config User provided connection config object
111
+ */
112
+ export function connectionInfoFromConfig(config) {
113
+ let connection = {
114
+ tls: false,
115
+ port: 0,
116
+ hostname: '',
117
+ direct: false,
118
+ direct_path: '',
119
+ };
120
+
121
+ let wsUri = config.websocket ?
122
+ matchUri(config.websocket) :
123
+ null;
124
+
125
+ if (wsUri) {
126
+ connection.direct = true;
127
+ connection.tls = ['wss', 'https', 'ircs'].indexOf(wsUri.protocol) > -1;
128
+ connection.port = wsUri.port;
129
+ connection.hostname = wsUri.hostname;
130
+ connection.direct_path = wsUri.path;
131
+ if (wsUri.search) {
132
+ connection.direct_path += '?' + wsUri.search;
133
+ }
134
+ } else {
135
+ connection.tls = config.tls;
136
+ connection.port = config.port;
137
+ connection.hostname = config.server;
138
+ connection.direct = !!config.direct;
139
+ connection.direct_path = config.direct_path || '';
140
+ }
141
+
142
+ return connection;
143
+ }
144
+
145
+ export function matchUri(uri) {
146
+ let reg = /(?:([a-z]+):\/\/)?([a-z.0-9-]+)(?::(?:(\+)?([0-9]+)))?(?:\/([^?]*))?(?:\?(.*))?/;
147
+ /*
148
+ 0: "ws://hostname.com:6676/erferf?foo=val"
149
+ 1: "ws"
150
+ 2: "hostname.com"
151
+ 3: undefined
152
+ 4: "6676"
153
+ 5: "erferf"
154
+ 6: "foo=var"
155
+ */
156
+ let m = uri.match(reg);
157
+ if (!m) {
158
+ return null;
159
+ }
160
+
161
+ return {
162
+ protocol: (m[1] || '').toLowerCase(),
163
+ hostname: m[2] || '',
164
+ port: parseIntZero(m[4] || ''),
165
+ path: '/' + (m[5] || ''),
166
+ search: m[6] || '',
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Parse a connection string into an object
172
+ * E.g. [ircs?://]irc.network.net:[+]6667/channel?nick=mynick;
173
+ * Multiple connections may be given, separated by ;
174
+ * @param {string} str The connection string URI
175
+ */
176
+ export function parseIrcUri(str) {
177
+ let reg = /(?:(ircs?|wss?):\/\/)?([a-z.0-9-]+)(?::(?:(\+)?([0-9]+)))?(?:\/([^?]*))?(?:\?(.*))?/;
178
+ let connections = [];
179
+ str.split(';').forEach((connectionString) => {
180
+ if (!connectionString) {
181
+ return;
182
+ }
183
+
184
+ let m = connectionString.match(reg);
185
+
186
+ if (!m) {
187
+ return;
188
+ }
189
+
190
+ let tls = m[1] === 'ircs' || m[1] === 'wss' || !!m[3];
191
+ let direct = m[1] === 'ws' || m[1] === 'wss';
192
+ let params = Object.create(null);
193
+ (m[6] || '').split('&').forEach((p) => {
194
+ let parts = p.split('=');
195
+ if (parts.length === 2) {
196
+ params[parts[0].toLowerCase()] = parts[1];
197
+ }
198
+ });
199
+
200
+ let channels = (m[5] || params.channel || '');
201
+ channels = _(channels.split(','))
202
+ .compact()
203
+ .map((_channelName) => {
204
+ let hasPrefix = _channelName[0] === '#' ||
205
+ _channelName[0] === '&';
206
+
207
+ let channelName = hasPrefix ?
208
+ _channelName :
209
+ '#' + _channelName;
210
+
211
+ return channelName;
212
+ });
213
+
214
+ connections.push({
215
+ tls: tls,
216
+ server: m[2],
217
+ port: parseInt(m[4] || (tls ? 6697 : 6667), 10),
218
+ channels: channels,
219
+ nick: params.nick || '',
220
+ encoding: (params.encoding || 'utf8'),
221
+ params: params,
222
+ direct: direct,
223
+ });
224
+ });
225
+
226
+ return connections;
227
+ }
228
+
229
+ /**
230
+ * Parse preset server string to an object
231
+ * format: freenode|irc.freenode.net:+6697
232
+ * @param {string} input Preset server string
233
+ */
234
+ export function parsePresetServer(input) {
235
+ let ret = {
236
+ name: '',
237
+ server: '',
238
+ port: 6667,
239
+ tls: false,
240
+ };
241
+
242
+ ret.toUri = () => `${ret.server}:${ret.tls ? '+' : ''}${ret.port}`;
243
+
244
+ let val = input;
245
+
246
+ let pipePos = val.indexOf('|');
247
+ if (pipePos > -1) {
248
+ ret.name = val.substr(0, pipePos);
249
+ val = val.substr(pipePos + 1);
250
+ }
251
+
252
+ let colonPos = val.indexOf(':');
253
+ if (colonPos === -1) {
254
+ ret.server = val;
255
+ val = '';
256
+ } else {
257
+ ret.server = val.substr(0, colonPos);
258
+ val = val.substr(colonPos + 1);
259
+ }
260
+
261
+ if (val[0] === '+') {
262
+ ret.tls = true;
263
+ val = val.substr(1);
264
+ }
265
+
266
+ if (val.length > 0) {
267
+ ret.port = parseInt(val, 10);
268
+ val = '';
269
+ }
270
+
271
+ if (!ret.name) {
272
+ ret.name = ret.server;
273
+ }
274
+
275
+ return ret;
276
+ }
277
+
278
+ /**
279
+ * Scan though an object and extend any dot notated keys
280
+ * @param {Object} confObj Source object to traverse
281
+ */
282
+ export function dedotObject(confObj, _place) {
283
+ let place = _place || [];
284
+ let regex = /\w\.\w/;
285
+
286
+ _.each(confObj, (val, key) => {
287
+ let ourPlace = place.concat([key]);
288
+ if (typeof val === 'object') {
289
+ dedotObject(confObj[key], ourPlace);
290
+ return;
291
+ }
292
+ if (regex.test(key)) {
293
+ delete confObj[key];
294
+ _.set(confObj, ourPlace.join('.'), val);
295
+ }
296
+ });
297
+ }
298
+
299
+ /**
300
+ * Replace the target object with source, while keeping the target object reference intact.
301
+ * Delete all the properties from the target object and copy the properties from source
302
+ * over to the target.
303
+ * a = {one: 1, two: 2, three: 3}
304
+ * b = {four: 4, five: 5, six: 6}
305
+ * replaceObjectProps(a, b)
306
+ * a.one === undefined;
307
+ * a.six === 6;
308
+ * @param {Object} target The target object that will be replaced
309
+ * @param {Object} source The source object from which all properties will be copied from
310
+ */
311
+ export function replaceObjectProps(target, source) {
312
+ Object.keys(target).forEach((prop) => delete target[prop]);
313
+ Object.keys(source).forEach((prop) => { target[prop] = source[prop]; });
314
+ }
315
+
316
+ /**
317
+ * Create an ISO8601 formatted date
318
+ * @param {Date} date The date object to create the time from. Defaults to the current time
319
+ */
320
+ export function dateIso(date) {
321
+ let d = date || new Date();
322
+ return strftimeUTC('%Y-%m-%dT%H:%M:%S.%LZ', d);
323
+ }
324
+
325
+ /**
326
+ * Cast a string to an int, returning 0 if it fails
327
+ * @param {String} inp The number as a string to convert to an int
328
+ */
329
+ export function parseIntZero(inp) {
330
+ let int = parseInt(inp, 10);
331
+ return Number.isNaN(int) ?
332
+ 0 :
333
+ int;
334
+ }
335
+
336
+ /**
337
+ * Prepare plugin provided object, for use as a plugin component
338
+ * @param {Number} pluginId The number to identify this plugin object
339
+ * @param {Component} componentOrElement The vue.js component object or html element
340
+ * @param {Object} args Optional arguments for this plugin { title: '', props: {} }
341
+ */
342
+ export function makePluginObject(pluginId, componentOrElement, args = {}) {
343
+ const plugin = {
344
+ id: pluginId,
345
+ component: null,
346
+ title: () => '',
347
+ props: {},
348
+ tabName: '',
349
+ };
350
+
351
+ // copy props from args
352
+ plugin.props = Object.assign(plugin.props, args.props);
353
+ plugin.tabName = args.tabName || args.title || 'plugin_tab_' + pluginId;
354
+
355
+ if (componentOrElement instanceof Element) {
356
+ // componentOrElement is an html element, so wrap it in a functional component
357
+
358
+ // eslint-disable-next-line no-underscore-dangle
359
+ if (componentOrElement.__vue__ && !window.kiwi_deprecations_vueEl) {
360
+ window.kiwi_deprecations_vueEl = true;
361
+ // eslint-disable-next-line no-console
362
+ console.warn('deprecated component.$el added to plugin api, please switch to just passing the vue.js component object');
363
+ }
364
+ plugin.component = PluginWrapper;
365
+ plugin.props = Object.assign(plugin.props, {
366
+ pluginElement: componentOrElement,
367
+ });
368
+ } else if (componentOrElement instanceof Object) {
369
+ // componentOrElement is an object, attempt to make a vue component from it
370
+ plugin.component = Vue.extend(componentOrElement);
371
+ } else {
372
+ plugin.component = componentOrElement;
373
+ }
374
+
375
+ if (typeof args.title === 'string') {
376
+ // if args.title exists wrap it in a function, that can also be a translation string
377
+ plugin.title = () => (args.title.slice(0, 2) === 't_' ? TextFormatting.t(args.title.slice(2)) : args.title);
378
+ }
379
+
380
+ return plugin;
381
+ }
382
+
383
+ // This provides a better sort for numbered nicks but does not work on ios9
384
+ let intlCollator;
385
+ if (global.Intl) {
386
+ intlCollator = new Intl.Collator({}, { numeric: true });
387
+ }
388
+
389
+ export function strCompare(a, b) {
390
+ if (intlCollator) {
391
+ return intlCollator.compare(a, b);
392
+ }
393
+
394
+ if (a === b) {
395
+ return 0;
396
+ }
397
+
398
+ return a > b ?
399
+ 1 :
400
+ -1;
401
+ }
@@ -0,0 +1,237 @@
1
+ 'kiwi public';
2
+
3
+ /** @module */
4
+ import _ from 'lodash';
5
+ import * as ipRegex from 'ip-regex';
6
+ import i18next from 'i18next';
7
+ import * as murmurhash3 from 'murmurhash3js';
8
+
9
+ import getState from '@/libs/state';
10
+ import ThemeManager from '@/libs/ThemeManager';
11
+
12
+ export const urlRegex = new RegExp(
13
+ // Detect either a protocol or 'www.' to start a URL
14
+ /(([A-Za-z][A-Za-z0-9-]*:\/\/)|(www\.))/.source +
15
+ '(' +
16
+ // Hostname and tld
17
+ /([\w\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF.-]+\.[a-zA-Z]{2,63})/.source +
18
+ '|' +
19
+ 'localhost' +
20
+ '|' +
21
+ // IPv4 address
22
+ ipRegex.v4().source +
23
+ '|' +
24
+ // IPv6 address
25
+ '(\\[?' +
26
+ ipRegex.v6().source +
27
+ '\\]?)' +
28
+ ')' +
29
+ // Optional port..
30
+ /(:[0-9]+)?/.source +
31
+ // Optional path..
32
+ /(\/[\w\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF!:.?$'()[\]*,;~+=&%@!\-/]*)?/.source +
33
+ // Optional fragment
34
+ /(#.*)?/.source,
35
+ 'i'
36
+ );
37
+
38
+ export const channelRegex = /(^|\s)([@+~&%}]*)([#&][^ ,\007<>\n\r]+?)([:;.,<>\n\r]+)?$/i;
39
+
40
+ export function linkifyChannels(word) {
41
+ // "@#kiwiirc," = 3 parts. (prefix=@)(channel=#kiwiirc)(suffix=,)
42
+ return word.replace(channelRegex, (match, mLead, mPrefix, mChannel, mSuffix) => {
43
+ let chan = _.escape(mChannel.trim());
44
+ let lead = _.escape(mLead);
45
+ let prefix = _.escape(mPrefix);
46
+ let suffix = _.escape(mSuffix);
47
+
48
+ let link = `<a class="u-link kiwi-channel" data-channel-name="${chan}">${chan}</a>`;
49
+ return `${lead}${prefix}${link}${suffix}`;
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Convert a nickname string to a colour code
55
+ * Uses these properties from the CSS theme file:
56
+ * --kiwi-nickcolour-count: 200; - number of available nick colours
57
+ * --kiwi-nickcolour-hueoffset: 0; - moves the hue value by hueoffset amounts
58
+ * --kiwi-nickcolour-saturation: 70; - the nick saturation in HSL value
59
+ * --kiwi-nickcolour-lightness: 40; - the nick lightness in HSL value
60
+ */
61
+ export function createNickColour(nick) {
62
+ let themeMngr = ThemeManager.instance();
63
+ let nickLower = (nick || '').toLowerCase();
64
+ let nickNum = murmurhash3.x86.hash32(nickLower);
65
+
66
+ let bucketSize = toInt(themeMngr.themeVar('nickcolour-count')) || 200;
67
+ let hueOffset = toInt(themeMngr.themeVar('nickcolour-hueoffset'));
68
+ let hsl = {
69
+ h: mapRange(Math.abs(nickNum) % bucketSize, 0, bucketSize, 0, 360) + hueOffset,
70
+ s: toInt(themeMngr.themeVar('nickcolour-saturation')) || 70,
71
+ l: toInt(themeMngr.themeVar('nickcolour-lightness')) || 40,
72
+ };
73
+ return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
74
+ }
75
+
76
+ /**
77
+ * Parse a string to an int, where NaN is 0
78
+ * @param {string} inp
79
+ */
80
+ export function toInt(inp) {
81
+ let int = parseInt(inp, 10);
82
+ if (Number.isNaN(int)) {
83
+ int = 0;
84
+ }
85
+
86
+ return int;
87
+ }
88
+
89
+ /**
90
+ * Extract a substring from a hex string and parse it as an integer
91
+ * @param {string} hash - Source hex string
92
+ * @param {number} index - Start index of substring
93
+ * @param {number} [length] - Length of substring. Defaults to 1.
94
+ */
95
+ export function hexVal(hash, index, len) {
96
+ return parseInt(hash.substr(index, len || 1), 16);
97
+ }
98
+
99
+ /*
100
+ * Re-maps a number from one range to another
101
+ * http://processing.org/reference/map_.html
102
+ */
103
+ export function mapRange(value, vMin, vMax, dMin, dMax) {
104
+ let vValue = parseFloat(value);
105
+ let vRange = vMax - vMin;
106
+ let dRange = dMax - dMin;
107
+
108
+ return (vValue - vMin) * dRange / vRange + dMin;
109
+ }
110
+
111
+ /**
112
+ * Format a string according to the configured textFormats, including a
113
+ * translation for the %text formatting variable
114
+ */
115
+ export function formatAndT(formatId, formatParams, localeId, localeParams) {
116
+ let body = t(localeId, localeParams);
117
+ if (formatParams) {
118
+ formatParams.text = body;
119
+ body = formatText(formatId, formatParams);
120
+ } else {
121
+ body = formatText(formatId, { text: body });
122
+ }
123
+
124
+ return body;
125
+ }
126
+
127
+ /**
128
+ * Create a user reference string similar to 'nick'
129
+ */
130
+ export function formatUser(fNick) {
131
+ let nick = fNick;
132
+
133
+ // Allow passing of a user object or irc-framework event
134
+ if (typeof nick === 'object') {
135
+ nick = nick.nick;
136
+ }
137
+
138
+ return formatText('user', { nick });
139
+ }
140
+
141
+ /**
142
+ * Create a full user reference similar to 'nick (user@host)'
143
+ */
144
+ export function formatUserFull(fNick, fUsername, fHost) {
145
+ let nick = '';
146
+ let username = '';
147
+ let host = '';
148
+
149
+ // Allow passing of a user object or irc-framework event
150
+ if (typeof fNick === 'object') {
151
+ let user = fNick;
152
+ nick = user.nick;
153
+ username = user.username || user.ident;
154
+ host = user.hostname || user.host;
155
+ } else {
156
+ nick = fNick;
157
+ username = fUsername;
158
+ host = fHost;
159
+ }
160
+
161
+ return formatText('user_full', { nick, username, host });
162
+ }
163
+
164
+ /**
165
+ * Format a string according to the configured textFormats
166
+ */
167
+ export function formatText(formatId, formatParams) {
168
+ let format = getState().setting('textFormats.' + formatId);
169
+ let params = formatParams;
170
+
171
+ // Most texts only have a 'text' variable so allow passing of a string for this
172
+ // variable as shorthand.
173
+ if (typeof params === 'string') {
174
+ params = { text: params };
175
+ }
176
+
177
+ // Expand a user mask into its individual parts (nick, ident, hostname)
178
+ if (params.user) {
179
+ params.nick = params.user.nick || '';
180
+ params.username = params.user.username || '';
181
+ params.host = params.user.hostname || '';
182
+ params.prefix = params.user.prefix || '';
183
+ }
184
+
185
+ // Do the magic. Use the %shorthand syntax to produce output.
186
+ let result = format.replace(/%([A-Z]{2,})/ig, (match, key) => {
187
+ let ret = '';
188
+ if (typeof params[key] !== 'undefined') {
189
+ ret = params[key];
190
+ }
191
+
192
+ return ret;
193
+ });
194
+
195
+ return result;
196
+ }
197
+
198
+ // Convert a given duration in seconds to human readable weeks,days,hours,minutes,seconds
199
+ // only showing the duration parts that are used eg 3666 --> 1 hour, 1 minute, 6 seconds
200
+ export function formatDuration(timeSeconds) {
201
+ let seconds = timeSeconds;
202
+
203
+ const weeks = Math.floor(seconds / (3600 * 24 * 7));
204
+ seconds -= weeks * 3600 * 24 * 7;
205
+
206
+ const days = Math.floor(seconds / (3600 * 24));
207
+ seconds -= days * 3600 * 24;
208
+
209
+ const hours = Math.floor(seconds / 3600);
210
+ seconds -= hours * 3600;
211
+
212
+ const minutes = Math.floor(seconds / 60);
213
+ seconds -= minutes * 60;
214
+
215
+ const tmp = [];
216
+ (weeks) && tmp.push(t('week', { count: weeks }));
217
+ (weeks || days) && tmp.push(t('day', { count: days }));
218
+ (days || hours) && tmp.push(t('hour', { count: hours }));
219
+ (days || hours || minutes) && tmp.push(t('minute', { count: minutes }));
220
+ tmp.push(t('second', { count: seconds }));
221
+
222
+ return tmp.join(' ');
223
+ }
224
+
225
+ export function formatNumber(num) {
226
+ return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
227
+ }
228
+
229
+ export function t(key, options) {
230
+ let val = i18next.t(key, options);
231
+ if (!val) {
232
+ let opts = options || {};
233
+ opts.lng = 'en-us';
234
+ val = i18next.t(key, opts);
235
+ }
236
+ return val;
237
+ }