diff --git a/.eslintrc.yml b/.eslintrc.yml
index 7633ddbf..2f9a28ec 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -3,7 +3,7 @@ root: true
parserOptions:
ecmaVersion: 2022
- parser: "babel-eslint"
+ parser: "@babel/eslint-parser",
env:
es6: true
diff --git a/client/dist/components/MessageTypes/index.js b/client/dist/components/MessageTypes/index.js
new file mode 100644
index 00000000..65e655e6
--- /dev/null
+++ b/client/dist/components/MessageTypes/index.js
@@ -0,0 +1,11 @@
+"use strict";
+// This creates a version of `require()` in the context of the current
+// directory, so we iterate over its content, which is a map statically built by
+// Webpack.
+// Second argument says it's recursive, third makes sure we only load templates.
+const requireViews = require.context(".", false, /\.vue$/);
+export default requireViews.keys().reduce((acc, path) => {
+ acc["message-" + path.substring(2, path.length - 4)] = requireViews(path).default;
+ return acc;
+}, {});
+//# sourceMappingURL=index.js.map
diff --git a/client/dist/components/MessageTypes/index.js.map b/client/dist/components/MessageTypes/index.js.map
new file mode 100644
index 00000000..68d4c677
--- /dev/null
+++ b/client/dist/components/MessageTypes/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../components/MessageTypes/index.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,sEAAsE;AACtE,gFAAgF;AAChF,WAAW;AACX,gFAAgF;AAChF,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAE3D,eAAe,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACvD,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;IAElF,OAAO,GAAG,CAAC;AACZ,CAAC,EAAE,EAAE,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/auth.js b/client/dist/js/auth.js
new file mode 100644
index 00000000..8ff55573
--- /dev/null
+++ b/client/dist/js/auth.js
@@ -0,0 +1,10 @@
+"use strict";
+import storage from "./localStorage";
+import location from "./location";
+export default class Auth {
+ static signout() {
+ storage.clear();
+ location.reload();
+ }
+}
+//# sourceMappingURL=auth.js.map
diff --git a/client/dist/js/auth.js.map b/client/dist/js/auth.js.map
new file mode 100644
index 00000000..e1ce5853
--- /dev/null
+++ b/client/dist/js/auth.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../js/auth.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,OAAO,MAAM,gBAAgB,CAAC;AACrC,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,CAAC,OAAO,OAAO,IAAI;IACxB,MAAM,CAAC,OAAO;QACb,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,QAAQ,CAAC,MAAM,EAAE,CAAC;IACnB,CAAC;CACD"}
\ No newline at end of file
diff --git a/client/dist/js/autocompletion.js b/client/dist/js/autocompletion.js
new file mode 100644
index 00000000..9d12a7ab
--- /dev/null
+++ b/client/dist/js/autocompletion.js
@@ -0,0 +1,280 @@
+"use strict";
+const constants = require("./constants");
+import Mousetrap from "mousetrap";
+import {Textcomplete} from "@textcomplete/core/dist/Textcomplete";
+import {TextareaEditor} from "@textcomplete/textarea/dist/TextareaEditor";
+import fuzzy from "fuzzy";
+import emojiMap from "./helpers/simplemap.json";
+import store from "./store";
+export default enableAutocomplete;
+const emojiSearchTerms = Object.keys(emojiMap);
+const emojiStrategy = {
+ id: "emoji",
+ match: /(^|\s):([-+\w:?]{2,}):?$/,
+ search(term, callback) {
+ // Trim colon from the matched term,
+ // as we are unable to get a clean string from match regex
+ term = term.replace(/:$/, "");
+ callback(fuzzyGrep(term, emojiSearchTerms));
+ },
+ template([string, original]) {
+ return `${emojiMap[original]} ${string}`;
+ },
+ replace([, original]) {
+ return "$1" + emojiMap[original];
+ },
+ index: 2,
+};
+const nicksStrategy = {
+ id: "nicks",
+ match: /(^|\s)(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
+ search(term, callback) {
+ term = term.slice(1);
+ if (term[0] === "@") {
+ callback(completeNicks(term.slice(1), true).map((val) => ["@" + val[0], "@" + val[1]]));
+ } else {
+ callback(completeNicks(term, true));
+ }
+ },
+ template([string]) {
+ return string;
+ },
+ replace([, original]) {
+ return "$1" + replaceNick(original);
+ },
+ index: 2,
+};
+const chanStrategy = {
+ id: "chans",
+ match: /(^|\s)((?:#|\+|&|![A-Z0-9]{5})(?:[^\s]+)?)$/,
+ search(term, callback) {
+ callback(completeChans(term));
+ },
+ template([string]) {
+ return string;
+ },
+ replace([, original]) {
+ return "$1" + original;
+ },
+ index: 2,
+};
+const commandStrategy = {
+ id: "commands",
+ match: /^\/(\w*)$/,
+ search(term, callback) {
+ callback(completeCommands("/" + term));
+ },
+ template([string]) {
+ return string;
+ },
+ replace([, original]) {
+ return original;
+ },
+ index: 1,
+};
+const foregroundColorStrategy = {
+ id: "foreground-colors",
+ match: /\x03(\d{0,2}|[A-Za-z ]{0,10})$/,
+ search(term, callback) {
+ term = term.toLowerCase();
+ const matchingColorCodes = constants.colorCodeMap
+ .filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
+ .map((i) => {
+ if (fuzzy.test(term, i[1])) {
+ return [
+ i[0],
+ fuzzy.match(term, i[1], {
+ pre: "",
+ post: "",
+ }).rendered,
+ ];
+ }
+ return i;
+ });
+ callback(matchingColorCodes);
+ },
+ template(value) {
+ return `${value[1]}`;
+ },
+ replace(value) {
+ return "\x03" + value[0];
+ },
+ index: 1,
+};
+const backgroundColorStrategy = {
+ id: "background-colors",
+ match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/,
+ search(term, callback, match) {
+ term = term.toLowerCase();
+ const matchingColorCodes = constants.colorCodeMap
+ .filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
+ .map((pair) => {
+ if (fuzzy.test(term, pair[1])) {
+ return [
+ pair[0],
+ fuzzy.match(term, pair[1], {
+ pre: "",
+ post: "",
+ }).rendered,
+ ];
+ }
+ return pair;
+ })
+ .map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`...
+ callback(matchingColorCodes);
+ },
+ template(value) {
+ return `${value[1]}`;
+ },
+ replace(value) {
+ return "\x03$1," + value[0];
+ },
+ index: 2,
+};
+function enableAutocomplete(input) {
+ let tabCount = 0;
+ let lastMatch = "";
+ let currentMatches = [];
+ input.addEventListener("input", (e) => {
+ if (e.detail === "autocomplete") {
+ return;
+ }
+ tabCount = 0;
+ currentMatches = [];
+ lastMatch = "";
+ });
+ Mousetrap(input).bind(
+ "tab",
+ (e) => {
+ if (store.state.isAutoCompleting) {
+ return;
+ }
+ e.preventDefault();
+ const text = input.value;
+ if (tabCount === 0) {
+ lastMatch = text.substring(0, input.selectionStart).split(/\s/).pop();
+ if (lastMatch.length === 0) {
+ return;
+ }
+ currentMatches = completeNicks(lastMatch, false);
+ if (currentMatches.length === 0) {
+ return;
+ }
+ }
+ const position = input.selectionStart - lastMatch.length;
+ const newMatch = replaceNick(
+ currentMatches[tabCount % currentMatches.length],
+ position
+ );
+ const remainder = text.substr(input.selectionStart);
+ input.value = text.substr(0, position) + newMatch + remainder;
+ input.selectionStart -= remainder.length;
+ input.selectionEnd = input.selectionStart;
+ // Propagate change to Vue model
+ input.dispatchEvent(
+ new CustomEvent("input", {
+ detail: "autocomplete",
+ })
+ );
+ lastMatch = newMatch;
+ tabCount++;
+ },
+ "keydown"
+ );
+ const strategies = [
+ emojiStrategy,
+ nicksStrategy,
+ chanStrategy,
+ commandStrategy,
+ foregroundColorStrategy,
+ backgroundColorStrategy,
+ ];
+ const editor = new TextareaEditor(input);
+ const textcomplete = new Textcomplete(editor, strategies, {
+ dropdown: {
+ className: "textcomplete-menu",
+ placement: "top",
+ },
+ });
+ textcomplete.on("show", () => {
+ store.commit("isAutoCompleting", true);
+ });
+ textcomplete.on("hidden", () => {
+ store.commit("isAutoCompleting", false);
+ });
+ return {
+ hide() {
+ textcomplete.hide();
+ },
+ destroy() {
+ textcomplete.destroy();
+ store.commit("isAutoCompleting", false);
+ },
+ };
+}
+function replaceNick(original, position = 1) {
+ // If no postfix specified, return autocompleted nick as-is
+ if (!store.state.settings.nickPostfix) {
+ return original;
+ }
+ // If there is whitespace in the input already, append space to nick
+ if (position > 0 && /\s/.test(store.state.activeChannel.channel.pendingMessage)) {
+ return original + " ";
+ }
+ // If nick is first in the input, append specified postfix
+ return original + store.state.settings.nickPostfix;
+}
+function fuzzyGrep(term, array) {
+ const results = fuzzy.filter(term, array, {
+ pre: "",
+ post: "",
+ });
+ return results.map((el) => [el.string, el.original]);
+}
+function rawNicks() {
+ if (store.state.activeChannel.channel.users.length > 0) {
+ const users = store.state.activeChannel.channel.users.slice();
+ return users.sort((a, b) => b.lastMessage - a.lastMessage).map((u) => u.nick);
+ }
+ const me = store.state.activeChannel.network.nick;
+ const otherUser = store.state.activeChannel.channel.name;
+ // If this is a query, add their name to autocomplete
+ if (me !== otherUser && store.state.activeChannel.channel.type === "query") {
+ return [otherUser, me];
+ }
+ // Return our own name by default for anything that isn't a channel or query
+ return [me];
+}
+function completeNicks(word, isFuzzy) {
+ const users = rawNicks();
+ word = word.toLowerCase();
+ if (isFuzzy) {
+ return fuzzyGrep(word, users);
+ }
+ return users.filter((w) => !w.toLowerCase().indexOf(word));
+}
+function getCommands() {
+ let cmds = constants.commands.slice();
+ if (!store.state.settings.searchEnabled) {
+ cmds = cmds.filter((c) => c !== "/search");
+ }
+ return cmds;
+}
+function completeCommands(word) {
+ const commands = getCommands();
+ return fuzzyGrep(word, commands);
+}
+function completeChans(word) {
+ const words = [];
+ for (const channel of store.state.activeChannel.network.channels) {
+ // Push all channels that start with the same CHANTYPE
+ if (channel.type === "channel" && channel.name[0] === word[0]) {
+ words.push(channel.name);
+ }
+ }
+ return fuzzyGrep(word, words);
+}
+//# sourceMappingURL=autocompletion.js.map
diff --git a/client/dist/js/autocompletion.js.map b/client/dist/js/autocompletion.js.map
new file mode 100644
index 00000000..d90df641
--- /dev/null
+++ b/client/dist/js/autocompletion.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"autocompletion.js","sourceRoot":"","sources":["../../js/autocompletion.js"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/client/dist/js/clipboard.js b/client/dist/js/clipboard.js
new file mode 100644
index 00000000..6a6a6bb8
--- /dev/null
+++ b/client/dist/js/clipboard.js
@@ -0,0 +1,25 @@
+"use strict";
+export default function (chat) {
+ // Disable in Firefox as it already copies flex text correctly
+ if (typeof window.InstallTrigger !== "undefined") {
+ return;
+ }
+ const selection = window.getSelection();
+ // If selection does not span multiple elements, do nothing
+ if (selection.anchorNode === selection.focusNode) {
+ return;
+ }
+ const range = selection.getRangeAt(0);
+ const documentFragment = range.cloneContents();
+ const div = document.createElement("div");
+ div.id = "js-copy-hack";
+ div.appendChild(documentFragment);
+ chat.appendChild(div);
+ selection.selectAllChildren(div);
+ window.setTimeout(() => {
+ chat.removeChild(div);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }, 0);
+}
+//# sourceMappingURL=clipboard.js.map
diff --git a/client/dist/js/clipboard.js.map b/client/dist/js/clipboard.js.map
new file mode 100644
index 00000000..d23987cb
--- /dev/null
+++ b/client/dist/js/clipboard.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../js/clipboard.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,CAAC,OAAO,WAAW,IAAI;IAC5B,8DAA8D;IAC9D,IAAI,OAAO,MAAM,CAAC,cAAc,KAAK,WAAW,EAAE;QACjD,OAAO;KACP;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;IAExC,2DAA2D;IAC3D,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,CAAC,SAAS,EAAE;QACjD,OAAO;KACP;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;IAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAE1C,GAAG,CAAC,EAAE,GAAG,cAAc,CAAC;IACxB,GAAG,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;IAClC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAEtB,SAAS,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAEjC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;QACtB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACtB,SAAS,CAAC,eAAe,EAAE,CAAC;QAC5B,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,CAAC,CAAC;AACP,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/commands/collapse.js b/client/dist/js/commands/collapse.js
new file mode 100644
index 00000000..910fc242
--- /dev/null
+++ b/client/dist/js/commands/collapse.js
@@ -0,0 +1,29 @@
+"use strict";
+import socket from "../socket";
+import store from "../store";
+function input() {
+ const messageIds = [];
+ for (const message of store.state.activeChannel.channel.messages) {
+ let toggled = false;
+ for (const preview of message.previews) {
+ if (preview.shown) {
+ preview.shown = false;
+ toggled = true;
+ }
+ }
+ if (toggled) {
+ messageIds.push(message.id);
+ }
+ }
+ // Tell the server we're toggling so it remembers at page reload
+ if (!document.body.classList.contains("public") && messageIds.length > 0) {
+ socket.emit("msg:preview:toggle", {
+ target: store.state.activeChannel.channel.id,
+ messageIds: messageIds,
+ shown: false,
+ });
+ }
+ return true;
+}
+export default {input};
+//# sourceMappingURL=collapse.js.map
diff --git a/client/dist/js/commands/collapse.js.map b/client/dist/js/commands/collapse.js.map
new file mode 100644
index 00000000..2781257a
--- /dev/null
+++ b/client/dist/js/commands/collapse.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"collapse.js","sourceRoot":"","sources":["../../../js/commands/collapse.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,KAAK,MAAM,UAAU,CAAC;AAE7B,SAAS,KAAK;IACb,MAAM,UAAU,GAAG,EAAE,CAAC;IAEtB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE;QACjE,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE;YACvC,IAAI,OAAO,CAAC,KAAK,EAAE;gBAClB,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;gBACtB,OAAO,GAAG,IAAI,CAAC;aACf;SACD;QAED,IAAI,OAAO,EAAE;YACZ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SAC5B;KACD;IAED,gEAAgE;IAChE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;QACzE,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;YACjC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;YAC5C,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;SACZ,CAAC,CAAC;KACH;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,eAAe,EAAC,KAAK,EAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/commands/expand.js b/client/dist/js/commands/expand.js
new file mode 100644
index 00000000..c1f8a1e9
--- /dev/null
+++ b/client/dist/js/commands/expand.js
@@ -0,0 +1,29 @@
+"use strict";
+import socket from "../socket";
+import store from "../store";
+function input() {
+ const messageIds = [];
+ for (const message of store.state.activeChannel.channel.messages) {
+ let toggled = false;
+ for (const preview of message.previews) {
+ if (!preview.shown) {
+ preview.shown = true;
+ toggled = true;
+ }
+ }
+ if (toggled) {
+ messageIds.push(message.id);
+ }
+ }
+ // Tell the server we're toggling so it remembers at page reload
+ if (!document.body.classList.contains("public") && messageIds.length > 0) {
+ socket.emit("msg:preview:toggle", {
+ target: store.state.activeChannel.channel.id,
+ messageIds: messageIds,
+ shown: true,
+ });
+ }
+ return true;
+}
+export default {input};
+//# sourceMappingURL=expand.js.map
diff --git a/client/dist/js/commands/expand.js.map b/client/dist/js/commands/expand.js.map
new file mode 100644
index 00000000..bba13c85
--- /dev/null
+++ b/client/dist/js/commands/expand.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"expand.js","sourceRoot":"","sources":["../../../js/commands/expand.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,KAAK,MAAM,UAAU,CAAC;AAE7B,SAAS,KAAK;IACb,MAAM,UAAU,GAAG,EAAE,CAAC;IAEtB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE;QACjE,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE;YACvC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBACnB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;gBACrB,OAAO,GAAG,IAAI,CAAC;aACf;SACD;QAED,IAAI,OAAO,EAAE;YACZ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SAC5B;KACD;IAED,gEAAgE;IAChE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;QACzE,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;YACjC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;YAC5C,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,IAAI;SACX,CAAC,CAAC;KACH;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,eAAe,EAAC,KAAK,EAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/commands/index.js b/client/dist/js/commands/index.js
new file mode 100644
index 00000000..f3265350
--- /dev/null
+++ b/client/dist/js/commands/index.js
@@ -0,0 +1,16 @@
+"use strict";
+// Taken from views/index.js
+// This creates a version of `require()` in the context of the current
+// directory, so we iterate over its content, which is a map statically built by
+// Webpack.
+// Second argument says it's recursive, third makes sure we only load javascript.
+const commands = require.context("./", true, /\.js$/);
+export default commands.keys().reduce((acc, path) => {
+ const command = path.substring(2, path.length - 3);
+ if (command === "index") {
+ return acc;
+ }
+ acc[command] = commands(path).default;
+ return acc;
+}, {});
+//# sourceMappingURL=index.js.map
diff --git a/client/dist/js/commands/index.js.map b/client/dist/js/commands/index.js.map
new file mode 100644
index 00000000..53dace1b
--- /dev/null
+++ b/client/dist/js/commands/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../js/commands/index.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,4BAA4B;AAE5B,sEAAsE;AACtE,gFAAgF;AAChF,WAAW;AACX,iFAAiF;AACjF,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAEtD,eAAe,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEnD,IAAI,OAAO,KAAK,OAAO,EAAE;QACxB,OAAO,GAAG,CAAC;KACX;IAED,GAAG,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;IAEtC,OAAO,GAAG,CAAC;AACZ,CAAC,EAAE,EAAE,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/commands/join.js b/client/dist/js/commands/join.js
new file mode 100644
index 00000000..59dfb281
--- /dev/null
+++ b/client/dist/js/commands/join.js
@@ -0,0 +1,40 @@
+"use strict";
+import socket from "../socket";
+import store from "../store";
+import {switchToChannel} from "../router";
+function input(args) {
+ if (args.length > 0) {
+ let channels = args[0];
+ if (channels.length > 0) {
+ const chanTypes = store.state.activeChannel.network.serverOptions.CHANTYPES;
+ const channelList = args[0].split(",");
+ if (chanTypes && chanTypes.length > 0) {
+ for (let c = 0; c < channelList.length; c++) {
+ if (!chanTypes.includes(channelList[c][0])) {
+ channelList[c] = chanTypes[0] + channelList[c];
+ }
+ }
+ }
+ channels = channelList.join(",");
+ const chan = store.getters.findChannelOnCurrentNetwork(channels);
+ if (chan) {
+ switchToChannel(chan);
+ } else {
+ socket.emit("input", {
+ text: `/join ${channels} ${args.length > 1 ? args[1] : ""}`,
+ target: store.state.activeChannel.channel.id,
+ });
+ return true;
+ }
+ }
+ } else if (store.state.activeChannel.channel.type === "channel") {
+ // If `/join` command is used without any arguments, re-join current channel
+ socket.emit("input", {
+ target: store.state.activeChannel.channel.id,
+ text: `/join ${store.state.activeChannel.channel.name}`,
+ });
+ return true;
+ }
+}
+export default {input};
+//# sourceMappingURL=join.js.map
diff --git a/client/dist/js/commands/join.js.map b/client/dist/js/commands/join.js.map
new file mode 100644
index 00000000..8e105ed9
--- /dev/null
+++ b/client/dist/js/commands/join.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"join.js","sourceRoot":"","sources":["../../../js/commands/join.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,KAAK,MAAM,UAAU,CAAC;AAC7B,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;AAE1C,SAAS,KAAK,CAAC,IAAI;IAClB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACpB,IAAI,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEvB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YACxB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC;YAC5E,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAEvC,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;oBAC5C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;wBAC3C,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;qBAC/C;iBACD;aACD;YAED,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEjC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,QAAQ,CAAC,CAAC;YAEjE,IAAI,IAAI,EAAE;gBACT,eAAe,CAAC,IAAI,CAAC,CAAC;aACtB;iBAAM;gBACN,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;oBACpB,IAAI,EAAE,SAAS,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBAC3D,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;iBAC5C,CAAC,CAAC;gBAEH,OAAO,IAAI,CAAC;aACZ;SACD;KACD;SAAM,IAAI,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE;QAChE,4EAA4E;QAC5E,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YACpB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;YAC5C,IAAI,EAAE,SAAS,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE;SACvD,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;KACZ;AACF,CAAC;AAED,eAAe,EAAC,KAAK,EAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/commands/search.js b/client/dist/js/commands/search.js
new file mode 100644
index 00000000..8e2e7247
--- /dev/null
+++ b/client/dist/js/commands/search.js
@@ -0,0 +1,20 @@
+"use strict";
+import store from "../store";
+import {router} from "../router";
+function input(args) {
+ if (!store.state.settings.searchEnabled) {
+ return false;
+ }
+ router.push({
+ name: "SearchResults",
+ params: {
+ id: store.state.activeChannel.channel.id,
+ },
+ query: {
+ q: args.join(" "),
+ },
+ });
+ return true;
+}
+export default {input};
+//# sourceMappingURL=search.js.map
diff --git a/client/dist/js/commands/search.js.map b/client/dist/js/commands/search.js.map
new file mode 100644
index 00000000..bad67269
--- /dev/null
+++ b/client/dist/js/commands/search.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../js/commands/search.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,MAAM,UAAU,CAAC;AAC7B,OAAO,EAAC,MAAM,EAAC,MAAM,WAAW,CAAC;AAEjC,SAAS,KAAK,CAAC,IAAI;IAClB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,EAAE;QACxC,OAAO,KAAK,CAAC;KACb;IAED,MAAM,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE;YACP,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;SACxC;QACD,KAAK,EAAE;YACN,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;SACjB;KACD,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACb,CAAC;AAED,eAAe,EAAC,KAAK,EAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/constants.js b/client/dist/js/constants.js
new file mode 100644
index 00000000..6dbfcf41
--- /dev/null
+++ b/client/dist/js/constants.js
@@ -0,0 +1,35 @@
+"use strict";
+const colorCodeMap = [
+ ["00", "White"],
+ ["01", "Black"],
+ ["02", "Blue"],
+ ["03", "Green"],
+ ["04", "Red"],
+ ["05", "Brown"],
+ ["06", "Magenta"],
+ ["07", "Orange"],
+ ["08", "Yellow"],
+ ["09", "Light Green"],
+ ["10", "Cyan"],
+ ["11", "Light Cyan"],
+ ["12", "Light Blue"],
+ ["13", "Pink"],
+ ["14", "Grey"],
+ ["15", "Light Grey"],
+];
+const condensedTypes = new Set(["chghost", "join", "part", "quit", "nick", "kick", "mode"]);
+const timeFormats = {
+ msgDefault: "HH:mm",
+ msgWithSeconds: "HH:mm:ss",
+ msg12h: "hh:mm A",
+ msg12hWithSeconds: "hh:mm:ss A",
+};
+export default {
+ colorCodeMap,
+ commands: [],
+ condensedTypes,
+ timeFormats,
+ // Same value as media query in CSS that forces sidebars to become overlays
+ mobileViewportPixels: 768,
+};
+//# sourceMappingURL=constants.js.map
diff --git a/client/dist/js/constants.js.map b/client/dist/js/constants.js.map
new file mode 100644
index 00000000..6b2257fe
--- /dev/null
+++ b/client/dist/js/constants.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../js/constants.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,YAAY,GAAG;IACpB,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,IAAI,EAAE,MAAM,CAAC;IACd,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,IAAI,EAAE,KAAK,CAAC;IACb,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,IAAI,EAAE,SAAS,CAAC;IACjB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,IAAI,EAAE,aAAa,CAAC;IACrB,CAAC,IAAI,EAAE,MAAM,CAAC;IACd,CAAC,IAAI,EAAE,YAAY,CAAC;IACpB,CAAC,IAAI,EAAE,YAAY,CAAC;IACpB,CAAC,IAAI,EAAE,MAAM,CAAC;IACd,CAAC,IAAI,EAAE,MAAM,CAAC;IACd,CAAC,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AAEF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5F,MAAM,WAAW,GAAG;IACnB,UAAU,EAAE,OAAO;IACnB,cAAc,EAAE,UAAU;IAC1B,MAAM,EAAE,SAAS;IACjB,iBAAiB,EAAE,YAAY;CAC/B,CAAC;AAEF,eAAe;IACd,YAAY;IACZ,QAAQ,EAAE,EAAE;IACZ,cAAc;IACd,WAAW;IACX,2EAA2E;IAC3E,oBAAoB,EAAE,GAAG;CACzB,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/eventbus.js b/client/dist/js/eventbus.js
new file mode 100644
index 00000000..a898b692
--- /dev/null
+++ b/client/dist/js/eventbus.js
@@ -0,0 +1,48 @@
+const events = new Map();
+class EventBus {
+ /**
+ * Register an event handler for the given type.
+ *
+ * @param {String} type Type of event to listen for.
+ * @param {Function} handler Function to call in response to given event.
+ */
+ on(type, handler) {
+ if (events.has(type)) {
+ events.get(type).push(handler);
+ } else {
+ events.set(type, [handler]);
+ }
+ }
+ /**
+ * Remove an event handler for the given type.
+ *
+ * @param {String} type Type of event to unregister `handler` from.
+ * @param {Function} handler Handler function to remove.
+ */
+ off(type, handler) {
+ if (events.has(type)) {
+ events.set(
+ type,
+ events.get(type).filter((item) => item !== handler)
+ );
+ }
+ }
+ /**
+ * Invoke all handlers for the given type.
+ *
+ * @param {String} type The event type to invoke.
+ * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler.
+ */
+ emit(type, ...evt) {
+ if (events.has(type)) {
+ events
+ .get(type)
+ .slice()
+ .map((handler) => {
+ handler(...evt);
+ });
+ }
+ }
+}
+export default new EventBus();
+//# sourceMappingURL=eventbus.js.map
diff --git a/client/dist/js/eventbus.js.map b/client/dist/js/eventbus.js.map
new file mode 100644
index 00000000..d66d7711
--- /dev/null
+++ b/client/dist/js/eventbus.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"eventbus.js","sourceRoot":"","sources":["../../js/eventbus.js"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;AAEzB,MAAM,QAAQ;IACb;;;;;OAKG;IACH,EAAE,CAAC,IAAI,EAAE,OAAO;QACf,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SAC/B;aAAM;YACN,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;SAC5B;IACF,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,OAAO;QAChB,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,CAAC,GAAG,CACT,IAAI,EACJ,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,CACnD,CAAC;SACF;IACF,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG;QAChB,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM;iBACJ,GAAG,CAAC,IAAI,CAAC;iBACT,KAAK,EAAE;iBACP,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAChB,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;YACjB,CAAC,CAAC,CAAC;SACJ;IACF,CAAC;CACD;AAED,eAAe,IAAI,QAAQ,EAAE,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/collapseNetwork.js b/client/dist/js/helpers/collapseNetwork.js
new file mode 100644
index 00000000..1dbf507e
--- /dev/null
+++ b/client/dist/js/helpers/collapseNetwork.js
@@ -0,0 +1,13 @@
+"use strict";
+import storage from "../localStorage";
+export default (network, isCollapsed) => {
+ const networks = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
+ network.isCollapsed = isCollapsed;
+ if (isCollapsed) {
+ networks.add(network.uuid);
+ } else {
+ networks.delete(network.uuid);
+ }
+ storage.set("thelounge.networks.collapsed", JSON.stringify([...networks]));
+};
+//# sourceMappingURL=collapseNetwork.js.map
diff --git a/client/dist/js/helpers/collapseNetwork.js.map b/client/dist/js/helpers/collapseNetwork.js.map
new file mode 100644
index 00000000..08dd97fa
--- /dev/null
+++ b/client/dist/js/helpers/collapseNetwork.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"collapseNetwork.js","sourceRoot":"","sources":["../../../js/helpers/collapseNetwork.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,OAAO,MAAM,iBAAiB,CAAC;AAEtC,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE;IACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC,CAAC,CAAC;IAClF,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;IAElC,IAAI,WAAW,EAAE;QAChB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3B;SAAM;QACN,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/colorClass.js b/client/dist/js/helpers/colorClass.js
new file mode 100644
index 00000000..2e62badb
--- /dev/null
+++ b/client/dist/js/helpers/colorClass.js
@@ -0,0 +1,15 @@
+"use strict";
+// Generates a string from "color-1" to "color-32" based on an input string
+export default (str) => {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ hash += str.charCodeAt(i);
+ }
+ /*
+ Modulo 32 lets us be case insensitive for ascii
+ due to A being ascii 65 (100 0001)
+ while a being ascii 97 (110 0001)
+ */
+ return "color-" + (1 + (hash % 32));
+};
+//# sourceMappingURL=colorClass.js.map
diff --git a/client/dist/js/helpers/colorClass.js.map b/client/dist/js/helpers/colorClass.js.map
new file mode 100644
index 00000000..0d382d86
--- /dev/null
+++ b/client/dist/js/helpers/colorClass.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"colorClass.js","sourceRoot":"","sources":["../../../js/helpers/colorClass.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,2EAA2E;AAC3E,eAAe,CAAC,GAAG,EAAE,EAAE;IACtB,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACpC,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;KAC1B;IAED;;;;MAIE;IACF,OAAO,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;AACrC,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/contextMenu.js b/client/dist/js/helpers/contextMenu.js
new file mode 100644
index 00000000..ec184686
--- /dev/null
+++ b/client/dist/js/helpers/contextMenu.js
@@ -0,0 +1,378 @@
+"use strict";
+import socket from "../socket";
+import eventbus from "../eventbus";
+export function generateChannelContextMenu($root, channel, network) {
+ const typeMap = {
+ lobby: "network",
+ channel: "chan",
+ query: "query",
+ special: "chan",
+ };
+ const closeMap = {
+ lobby: "Remove",
+ channel: "Leave",
+ query: "Close",
+ special: "Close",
+ };
+ let items = [
+ {
+ label: channel.name,
+ type: "item",
+ class: typeMap[channel.type],
+ link: `/chan-${channel.id}`,
+ },
+ {
+ type: "divider",
+ },
+ ];
+ // Add menu items for lobbies
+ if (channel.type === "lobby") {
+ items = [
+ ...items,
+ {
+ label: "Edit this network…",
+ type: "item",
+ class: "edit",
+ link: `/edit-network/${network.uuid}`,
+ },
+ {
+ label: "Join a channel…",
+ type: "item",
+ class: "join",
+ action: () => (network.isJoinChannelShown = true),
+ },
+ {
+ label: "List all channels",
+ type: "item",
+ class: "list",
+ action: () =>
+ socket.emit("input", {
+ target: channel.id,
+ text: "/list",
+ }),
+ },
+ {
+ label: "List ignored users",
+ type: "item",
+ class: "list",
+ action: () =>
+ socket.emit("input", {
+ target: channel.id,
+ text: "/ignorelist",
+ }),
+ },
+ network.status.connected
+ ? {
+ label: "Disconnect",
+ type: "item",
+ class: "disconnect",
+ action: () =>
+ socket.emit("input", {
+ target: channel.id,
+ text: "/disconnect",
+ }),
+ }
+ : {
+ label: "Connect",
+ type: "item",
+ class: "connect",
+ action: () =>
+ socket.emit("input", {
+ target: channel.id,
+ text: "/connect",
+ }),
+ },
+ ];
+ }
+ // Add menu items for channels
+ if (channel.type === "channel") {
+ items.push({
+ label: "Edit topic",
+ type: "item",
+ class: "edit",
+ action() {
+ channel.editTopic = true;
+ $root.switchToChannel(channel);
+ },
+ });
+ items.push({
+ label: "List banned users",
+ type: "item",
+ class: "list",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/banlist",
+ });
+ },
+ });
+ }
+ // Add menu items for queries
+ if (channel.type === "query") {
+ items.push(
+ {
+ label: "User information",
+ type: "item",
+ class: "action-whois",
+ action() {
+ $root.switchToChannel(channel);
+ socket.emit("input", {
+ target: channel.id,
+ text: "/whois " + channel.name,
+ });
+ },
+ },
+ {
+ label: "Ignore user",
+ type: "item",
+ class: "action-ignore",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/ignore " + channel.name,
+ });
+ },
+ }
+ );
+ }
+ if (channel.type === "channel" || channel.type === "query") {
+ items.push({
+ label: "Clear history",
+ type: "item",
+ class: "clear-history",
+ action() {
+ eventbus.emit(
+ "confirm-dialog",
+ {
+ title: "Clear history",
+ text: `Are you sure you want to clear history for ${channel.name}? This cannot be undone.`,
+ button: "Clear history",
+ },
+ (result) => {
+ if (!result) {
+ return;
+ }
+ socket.emit("history:clear", {
+ target: channel.id,
+ });
+ }
+ );
+ },
+ });
+ }
+ const humanFriendlyChanTypeMap = {
+ lobby: "network",
+ channel: "channel",
+ query: "conversation",
+ };
+ // We don't allow the muting of ChanType.SPECIAL channels
+ const mutableChanTypes = Object.keys(humanFriendlyChanTypeMap);
+ if (mutableChanTypes.includes(channel.type)) {
+ const chanType = humanFriendlyChanTypeMap[channel.type];
+ items.push({
+ label: channel.muted ? `Unmute ${chanType}` : `Mute ${chanType}`,
+ type: "item",
+ class: "mute",
+ action() {
+ socket.emit("mute:change", {
+ target: channel.id,
+ setMutedTo: !channel.muted,
+ });
+ },
+ });
+ }
+ // Add close menu item
+ items.push({
+ label: closeMap[channel.type],
+ type: "item",
+ class: "close",
+ action() {
+ $root.closeChannel(channel);
+ },
+ });
+ return items;
+}
+export function generateInlineChannelContextMenu($root, chan, network) {
+ const join = () => {
+ const channel = network.channels.find((c) => c.name === chan);
+ if (channel) {
+ $root.switchToChannel(channel);
+ }
+ socket.emit("input", {
+ target: $root.$store.state.activeChannel.channel.id,
+ text: "/join " + chan,
+ });
+ };
+ const channel = network.channels.find((c) => c.name === chan);
+ if (channel) {
+ return [
+ {
+ label: "Go to channel",
+ type: "item",
+ class: "chan",
+ link: `/chan-${channel.id}`,
+ },
+ ];
+ }
+ return [
+ {
+ label: "Join channel",
+ type: "item",
+ class: "join",
+ action: join,
+ },
+ ];
+}
+export function generateUserContextMenu($root, channel, network, user) {
+ const currentChannelUser = channel
+ ? channel.users.find((u) => u.nick === network.nick) || {}
+ : {};
+ const whois = () => {
+ const chan = network.channels.find((c) => c.name === user.nick);
+ if (chan) {
+ $root.switchToChannel(chan);
+ }
+ socket.emit("input", {
+ target: channel.id,
+ text: "/whois " + user.nick,
+ });
+ };
+ const items = [
+ {
+ label: user.nick,
+ type: "item",
+ class: "user",
+ action: whois,
+ },
+ {
+ type: "divider",
+ },
+ {
+ label: "User information",
+ type: "item",
+ class: "action-whois",
+ action: whois,
+ },
+ {
+ label: "Ignore user",
+ type: "item",
+ class: "action-ignore",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/ignore " + user.nick,
+ });
+ },
+ },
+ {
+ label: "Direct messages",
+ type: "item",
+ class: "action-query",
+ action() {
+ const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick);
+ if (chan) {
+ $root.switchToChannel(chan);
+ }
+ socket.emit("input", {
+ target: channel.id,
+ text: "/query " + user.nick,
+ });
+ },
+ },
+ ];
+ // Bail because we're in a query or we don't have a special mode.
+ if (!currentChannelUser.modes || currentChannelUser.modes.length < 1) {
+ return items;
+ }
+ // Names of the standard modes we are able to change
+ const modeCharToName = {
+ "~": "owner",
+ "&": "admin",
+ "@": "operator",
+ "%": "half-op",
+ "+": "voice",
+ };
+ // Labels for the mode changes. For example .rev({mode: "a", symbol: "&"}) => 'Revoke admin (-a)'
+ const modeTextTemplate = {
+ revoke(m) {
+ const name = modeCharToName[m.symbol];
+ const res = name ? `Revoke ${name} (-${m.mode})` : `Mode -${m.mode}`;
+ return res;
+ },
+ give(m) {
+ const name = modeCharToName[m.symbol];
+ const res = name ? `Give ${name} (+${m.mode})` : `Mode +${m.mode}`;
+ return res;
+ },
+ };
+ const networkModeSymbols = network.serverOptions.PREFIX.symbols;
+ /**
+ * Determine whether the prefix of mode p1 has access to perform actions on p2.
+ *
+ * EXAMPLE:
+ * compare('@', '@') => true
+ * compare('&', '@') => true
+ * compare('+', '~') => false
+ * @param {string} p1 The mode performing an action
+ * @param {string} p2 The target mode
+ *
+ * @return {boolean} whether p1 can perform an action on p2
+ */
+ function compare(p1, p2) {
+ // The modes ~ and @ can perform actions on their own mode. The others on modes below.
+ return "~@".indexOf(p1) > -1
+ ? networkModeSymbols.indexOf(p1) <= networkModeSymbols.indexOf(p2)
+ : networkModeSymbols.indexOf(p1) < networkModeSymbols.indexOf(p2);
+ }
+ network.serverOptions.PREFIX.prefix.forEach((mode) => {
+ if (!compare(currentChannelUser.modes[0], mode.symbol)) {
+ // Our highest mode is below the current mode. Bail.
+ return;
+ }
+ if (!user.modes.includes(mode.symbol)) {
+ // The target doesn't already have this mode, therefore we can set it.
+ items.push({
+ label: modeTextTemplate.give(mode),
+ type: "item",
+ class: "action-set-mode",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/mode +" + mode.mode + " " + user.nick,
+ });
+ },
+ });
+ } else {
+ items.push({
+ label: modeTextTemplate.revoke(mode),
+ type: "item",
+ class: "action-revoke-mode",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/mode -" + mode.mode + " " + user.nick,
+ });
+ },
+ });
+ }
+ });
+ // Determine if we are half-op or op depending on the network modes so we can kick.
+ if (!compare(networkModeSymbols.indexOf("%") > -1 ? "%" : "@", currentChannelUser.modes[0])) {
+ // Check if the target user has no mode or a mode lower than ours.
+ if (user.modes.length === 0 || compare(currentChannelUser.modes[0], user.modes[0])) {
+ items.push({
+ label: "Kick",
+ type: "item",
+ class: "action-kick",
+ action() {
+ socket.emit("input", {
+ target: channel.id,
+ text: "/kick " + user.nick,
+ });
+ },
+ });
+ }
+ }
+ return items;
+}
+//# sourceMappingURL=contextMenu.js.map
diff --git a/client/dist/js/helpers/contextMenu.js.map b/client/dist/js/helpers/contextMenu.js.map
new file mode 100644
index 00000000..72783177
--- /dev/null
+++ b/client/dist/js/helpers/contextMenu.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"contextMenu.js","sourceRoot":"","sources":["../../../js/helpers/contextMenu.js"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/distance.js b/client/dist/js/helpers/distance.js
new file mode 100644
index 00000000..670693ca
--- /dev/null
+++ b/client/dist/js/helpers/distance.js
@@ -0,0 +1,5 @@
+function distance([x1, y1], [x2, y2]) {
+ return Math.hypot(x1 - x2, y1 - y2);
+}
+export default distance;
+//# sourceMappingURL=distance.js.map
diff --git a/client/dist/js/helpers/distance.js.map b/client/dist/js/helpers/distance.js.map
new file mode 100644
index 00000000..02f5b8d9
--- /dev/null
+++ b/client/dist/js/helpers/distance.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"distance.js","sourceRoot":"","sources":["../../../js/helpers/distance.js"],"names":[],"mappings":"AAAA,SAAS,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,eAAe,QAAQ,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/friendlysize.js b/client/dist/js/helpers/friendlysize.js
new file mode 100644
index 00000000..db1a4686
--- /dev/null
+++ b/client/dist/js/helpers/friendlysize.js
@@ -0,0 +1,9 @@
+"use strict";
+const sizes = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"];
+export default (size) => {
+ // Loosely inspired from https://stackoverflow.com/a/18650828/1935861
+ const i = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0;
+ const fixedSize = parseFloat((size / Math.pow(1024, i)).toFixed(1));
+ return `${fixedSize} ${sizes[i]}`;
+};
+//# sourceMappingURL=friendlysize.js.map
diff --git a/client/dist/js/helpers/friendlysize.js.map b/client/dist/js/helpers/friendlysize.js.map
new file mode 100644
index 00000000..f7255291
--- /dev/null
+++ b/client/dist/js/helpers/friendlysize.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"friendlysize.js","sourceRoot":"","sources":["../../../js/helpers/friendlysize.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAE3D,eAAe,CAAC,IAAI,EAAE,EAAE;IACvB,qEAAqE;IACrE,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/anyIntersection.js b/client/dist/js/helpers/ircmessageparser/anyIntersection.js
new file mode 100644
index 00000000..9c041e6a
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/anyIntersection.js
@@ -0,0 +1,13 @@
+"use strict";
+// Return true if any section of "a" or "b" parts (defined by their start/end
+// markers) intersect each other, false otherwise.
+function anyIntersection(a, b) {
+ return (
+ (a.start <= b.start && b.start < a.end) ||
+ (a.start < b.end && b.end <= a.end) ||
+ (b.start <= a.start && a.start < b.end) ||
+ (b.start < a.end && a.end <= b.end)
+ );
+}
+export default anyIntersection;
+//# sourceMappingURL=anyIntersection.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/anyIntersection.js.map b/client/dist/js/helpers/ircmessageparser/anyIntersection.js.map
new file mode 100644
index 00000000..97b2bb3c
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/anyIntersection.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"anyIntersection.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/anyIntersection.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,6EAA6E;AAC7E,kDAAkD;AAClD,SAAS,eAAe,CAAC,CAAC,EAAE,CAAC;IAC5B,OAAO,CACN,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;QACvC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC;QACnC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;QACvC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CACnC,CAAC;AACH,CAAC;AAED,eAAe,eAAe,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js b/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js
new file mode 100644
index 00000000..c612fef6
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js
@@ -0,0 +1,5 @@
+"use strict";
+const matchFormatting =
+ /\x02|\x1D|\x1F|\x16|\x0F|\x11|\x1E|\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?|\x04(?:[0-9a-f]{6}(?:,[0-9a-f]{6})?)?/gi;
+module.exports = (message) => message.replace(matchFormatting, "").trim();
+//# sourceMappingURL=cleanIrcMessage.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js.map b/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js.map
new file mode 100644
index 00000000..5bc423d4
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/cleanIrcMessage.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"cleanIrcMessage.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/cleanIrcMessage.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,eAAe,GACpB,gHAAgH,CAAC;AAElH,MAAM,CAAC,OAAO,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/fill.js b/client/dist/js/helpers/ircmessageparser/fill.js
new file mode 100644
index 00000000..0c82cb26
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/fill.js
@@ -0,0 +1,30 @@
+"use strict";
+// Create plain text entries corresponding to areas of the text that match no
+// existing entries. Returns an empty array if all parts of the text have been
+// parsed into recognizable entries already.
+function fill(existingEntries, text) {
+ let position = 0;
+ // Fill inner parts of the text. For example, if text is `foobarbaz` and both
+ // `foo` and `baz` have matched into an entry, this will return a dummy entry
+ // corresponding to `bar`.
+ const result = existingEntries.reduce((acc, textSegment) => {
+ if (textSegment.start > position) {
+ acc.push({
+ start: position,
+ end: textSegment.start,
+ });
+ }
+ position = textSegment.end;
+ return acc;
+ }, []);
+ // Complete the unmatched end of the text with a dummy entry
+ if (position < text.length) {
+ result.push({
+ start: position,
+ end: text.length,
+ });
+ }
+ return result;
+}
+export default fill;
+//# sourceMappingURL=fill.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/fill.js.map b/client/dist/js/helpers/ircmessageparser/fill.js.map
new file mode 100644
index 00000000..c85edec6
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/fill.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"fill.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/fill.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,6EAA6E;AAC7E,8EAA8E;AAC9E,4CAA4C;AAC5C,SAAS,IAAI,CAAC,eAAe,EAAE,IAAI;IAClC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,6EAA6E;IAC7E,6EAA6E;IAC7E,0BAA0B;IAC1B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE;QAC1D,IAAI,WAAW,CAAC,KAAK,GAAG,QAAQ,EAAE;YACjC,GAAG,CAAC,IAAI,CAAC;gBACR,KAAK,EAAE,QAAQ;gBACf,GAAG,EAAE,WAAW,CAAC,KAAK;aACtB,CAAC,CAAC;SACH;QAED,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC;QAC3B,OAAO,GAAG,CAAC;IACZ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4DAA4D;IAC5D,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE;QAC3B,MAAM,CAAC,IAAI,CAAC;YACX,KAAK,EAAE,QAAQ;YACf,GAAG,EAAE,IAAI,CAAC,MAAM;SAChB,CAAC,CAAC;KACH;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,IAAI,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/findChannels.js b/client/dist/js/helpers/ircmessageparser/findChannels.js
new file mode 100644
index 00000000..694e187f
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findChannels.js
@@ -0,0 +1,37 @@
+"use strict";
+// Escapes the RegExp special characters "^", "$", "", ".", "*", "+", "?", "(",
+// ")", "[", "]", "{", "}", and "|" in string.
+// See https://lodash.com/docs/#escapeRegExp
+import escapeRegExp from "lodash/escapeRegExp";
+// Given an array of channel prefixes (such as "#" and "&") and an array of user
+// modes (such as "@" and "+"), this function extracts channels and nicks from a
+// text.
+// It returns an array of objects for each channel found with their start index,
+// end index and channel name.
+function findChannels(text, channelPrefixes, userModes) {
+ // `userModePattern` is necessary to ignore user modes in /whois responses.
+ // For example, a voiced user in #thelounge will have a /whois response of:
+ // > foo is on the following channels: +#thelounge
+ // We need to explicitly ignore user modes to parse such channels correctly.
+ const userModePattern = userModes.map(escapeRegExp).join("");
+ const channelPrefixPattern = channelPrefixes.map(escapeRegExp).join("");
+ const channelPattern = `(?:^|\\s)[${userModePattern}]*([${channelPrefixPattern}][^ \u0007]+)`;
+ const channelRegExp = new RegExp(channelPattern, "g");
+ const result = [];
+ let match;
+ do {
+ // With global ("g") regexes, calling `exec` multiple times will find
+ // successive matches in the same string.
+ match = channelRegExp.exec(text);
+ if (match) {
+ result.push({
+ start: match.index + match[0].length - match[1].length,
+ end: match.index + match[0].length,
+ channel: match[1],
+ });
+ }
+ } while (match);
+ return result;
+}
+export default findChannels;
+//# sourceMappingURL=findChannels.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/findChannels.js.map b/client/dist/js/helpers/ircmessageparser/findChannels.js.map
new file mode 100644
index 00000000..c26c9407
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findChannels.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"findChannels.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/findChannels.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,+EAA+E;AAC/E,8CAA8C;AAC9C,4CAA4C;AAC5C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,gFAAgF;AAChF,gFAAgF;AAChF,QAAQ;AACR,gFAAgF;AAChF,8BAA8B;AAC9B,SAAS,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,SAAS;IACrD,2EAA2E;IAC3E,2EAA2E;IAC3E,kDAAkD;IAClD,4EAA4E;IAC5E,MAAM,eAAe,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,MAAM,oBAAoB,GAAG,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,aAAa,eAAe,OAAO,oBAAoB,eAAe,CAAC;IAC9F,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,IAAI,KAAK,CAAC;IAEV,GAAG;QACF,qEAAqE;QACrE,yCAAyC;QACzC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,IAAI,KAAK,EAAE;YACV,MAAM,CAAC,IAAI,CAAC;gBACX,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;gBACtD,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;gBAClC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;aACjB,CAAC,CAAC;SACH;KACD,QAAQ,KAAK,EAAE;IAEhB,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,YAAY,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/findEmoji.js b/client/dist/js/helpers/ircmessageparser/findEmoji.js
new file mode 100644
index 00000000..08bd843d
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findEmoji.js
@@ -0,0 +1,16 @@
+"use strict";
+const emojiRegExp = require("emoji-regex")();
+function findEmoji(text) {
+ const result = [];
+ let match;
+ while ((match = emojiRegExp.exec(text))) {
+ result.push({
+ start: match.index,
+ end: match.index + match[0].length,
+ emoji: match[0],
+ });
+ }
+ return result;
+}
+export default findEmoji;
+//# sourceMappingURL=findEmoji.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/findEmoji.js.map b/client/dist/js/helpers/ircmessageparser/findEmoji.js.map
new file mode 100644
index 00000000..cbfb0c73
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findEmoji.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"findEmoji.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/findEmoji.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;AAE7C,SAAS,SAAS,CAAC,IAAI;IACtB,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,IAAI,KAAK,CAAC;IAEV,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;QACxC,MAAM,CAAC,IAAI,CAAC;YACX,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;YAClC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;SACf,CAAC,CAAC;KACH;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,SAAS,CAAC"}
\ No newline at end of file
diff --git a/client/js/helpers/ircmessageparser/findLinks.js b/client/dist/js/helpers/ircmessageparser/findLinks.js
similarity index 88%
rename from client/js/helpers/ircmessageparser/findLinks.js
rename to client/dist/js/helpers/ircmessageparser/findLinks.js
index b35a3efe..00c3bc37 100644
--- a/client/js/helpers/ircmessageparser/findLinks.js
+++ b/client/dist/js/helpers/ircmessageparser/findLinks.js
@@ -1,27 +1,23 @@
"use strict";
-
-const LinkifyIt = require("linkify-it");
-
+import LinkifyIt from "linkify-it";
LinkifyIt.prototype.normalize = function normalize(match) {
if (!match.schema) {
match.schema = "http:";
match.url = "http://" + match.url;
+ //@ts-ignore
match.noschema = true;
}
-
if (match.schema === "//") {
match.schema = "http:";
match.url = "http:" + match.url;
+ //@ts-ignore
match.noschema = true;
}
-
if (match.schema === "mailto:" && !/^mailto:/i.test(match.url)) {
match.url = "mailto:" + match.url;
}
};
-
const linkify = LinkifyIt().tlds(require("tlds")).tlds("onion", true);
-
// Known schemes to detect in text
const commonSchemes = [
"sftp",
@@ -39,31 +35,23 @@ const commonSchemes = [
"gopher",
"gemini",
];
-
for (const schema of commonSchemes) {
linkify.add(schema + ":", "http:");
}
-
function findLinks(text) {
const matches = linkify.match(text);
-
if (!matches) {
return [];
}
-
return matches.map(returnUrl);
}
-
function findLinksWithSchema(text) {
const matches = linkify.match(text);
-
if (!matches) {
return [];
}
-
return matches.filter((url) => !url.noschema).map(returnUrl);
}
-
function returnUrl(url) {
return {
start: url.index,
@@ -71,8 +59,5 @@ function returnUrl(url) {
link: url.url,
};
}
-
-module.exports = {
- findLinks,
- findLinksWithSchema,
-};
+export {findLinks, findLinksWithSchema};
+//# sourceMappingURL=findLinks.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/findLinks.js.map b/client/dist/js/helpers/ircmessageparser/findLinks.js.map
new file mode 100644
index 00000000..e939235e
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findLinks.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"findLinks.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/findLinks.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,SAAoB,MAAM,YAAY,CAAA;AAM7C,SAAS,CAAC,SAAS,CAAC,SAAS,GAAG,SAAS,SAAS,CAAC,KAAe;IACjE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;QAClB,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;QACvB,KAAK,CAAC,GAAG,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;QAClC,YAAY;QACZ,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;KACtB;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE;QAC1B,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;QACvB,KAAK,CAAC,GAAG,GAAG,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC;QAChC,YAAY;QACZ,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;KACtB;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QAC/D,KAAK,CAAC,GAAG,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;KAClC;AACF,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAEtE,kCAAkC;AAClC,MAAM,aAAa,GAAG;IACrB,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,OAAO;IACP,QAAQ;IACR,WAAW;IACX,SAAS;IACT,KAAK;IACL,QAAQ;IACR,QAAQ;CACR,CAAC;AAEF,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE;IACnC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,SAAS,SAAS,CAAC,IAAY;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;IAElD,IAAI,CAAC,OAAO,EAAE;QACb,OAAO,EAAE,CAAC;KACV;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;IAElD,IAAI,CAAC,OAAO,EAAE;QACb,OAAO,EAAE,CAAC;KACV;IAED,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,SAAS,CAAC,GAAa;IAC/B,OAAO;QACN,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,EAAE,GAAG,CAAC,SAAS;QAClB,IAAI,EAAE,GAAG,CAAC,GAAG;KACb,CAAC;AACH,CAAC;AAED,OAAO,EACN,SAAS,EACT,mBAAmB,GACnB,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/findNames.js b/client/dist/js/helpers/ircmessageparser/findNames.js
new file mode 100644
index 00000000..89688e2b
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findNames.js
@@ -0,0 +1,22 @@
+"use strict";
+const nickRegExp = /([\w[\]\\`^{|}-]+)/g;
+function findNames(text, users) {
+ const result = [];
+ // Return early if we don't have any nicknames to find
+ if (users.length === 0) {
+ return result;
+ }
+ let match;
+ while ((match = nickRegExp.exec(text))) {
+ if (users.indexOf(match[1]) > -1) {
+ result.push({
+ start: match.index,
+ end: match.index + match[1].length,
+ nick: match[1],
+ });
+ }
+ }
+ return result;
+}
+export default findNames;
+//# sourceMappingURL=findNames.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/findNames.js.map b/client/dist/js/helpers/ircmessageparser/findNames.js.map
new file mode 100644
index 00000000..bdddf9dc
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/findNames.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"findNames.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/findNames.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAEzC,SAAS,SAAS,CAAC,IAAI,EAAE,KAAK;IAC7B,MAAM,MAAM,GAAG,EAAE,CAAC;IAElB,sDAAsD;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;QACvB,OAAO,MAAM,CAAC;KACd;IAED,IAAI,KAAK,CAAC;IAEV,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE;YACjC,MAAM,CAAC,IAAI,CAAC;gBACX,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;gBAClC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;aACd,CAAC,CAAC;SACH;KACD;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,eAAe,SAAS,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/merge.js b/client/dist/js/helpers/ircmessageparser/merge.js
new file mode 100644
index 00000000..8d0a6faa
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/merge.js
@@ -0,0 +1,44 @@
+"use strict";
+import anyIntersection from "./anyIntersection";
+import fill from "./fill";
+// Merge text part information within a styling fragment
+function assign(textPart, fragment) {
+ const fragStart = fragment.start;
+ const start = Math.max(fragment.start, textPart.start);
+ const end = Math.min(fragment.end, textPart.end);
+ const text = fragment.text.slice(start - fragStart, end - fragStart);
+ return Object.assign({}, fragment, {start, end, text});
+}
+function sortParts(a, b) {
+ return a.start - b.start || b.end - a.end;
+}
+// Merge the style fragments within the text parts, taking into account
+// boundaries and text sections that have not matched to links or channels.
+// For example, given a string "foobar" where "foo" and "bar" have been
+// identified as parts (channels, links, etc.) and "fo", "ob" and "ar" have 3
+// different styles, the first resulting part will contain fragments "fo" and
+// "o", and the second resulting part will contain "b" and "ar". "o" and "b"
+// fragments will contain duplicate styling attributes.
+function merge(textParts, styleFragments, cleanText) {
+ // Remove overlapping parts
+ textParts = textParts.sort(sortParts).reduce((prev, curr) => {
+ const intersection = prev.some((p) => anyIntersection(p, curr));
+ if (intersection) {
+ return prev;
+ }
+ return prev.concat([curr]);
+ }, []);
+ // Every section of the original text that has not been captured in a "part"
+ // is filled with "text" parts, dummy objects with start/end but no extra
+ // metadata.
+ const allParts = textParts.concat(fill(textParts, cleanText)).sort(sortParts); // Sort all parts identified based on their position in the original text
+ // Distribute the style fragments within the text parts
+ return allParts.map((textPart) => {
+ textPart.fragments = styleFragments
+ .filter((fragment) => anyIntersection(textPart, fragment))
+ .map((fragment) => assign(textPart, fragment));
+ return textPart;
+ });
+}
+export default merge;
+//# sourceMappingURL=merge.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/merge.js.map b/client/dist/js/helpers/ircmessageparser/merge.js.map
new file mode 100644
index 00000000..50e81779
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/merge.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/merge.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,IAAI,MAAM,QAAQ,CAAC;AAE1B,wDAAwD;AACxD,SAAS,MAAM,CAAC,QAAQ,EAAE,QAAQ;IACjC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,SAAS,EAAE,GAAG,GAAG,SAAS,CAAC,CAAC;IAErE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC;IACtB,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uDAAuD;AACvD,SAAS,KAAK,CAAC,SAAS,EAAE,cAAc,EAAE,SAAS;IAClD,2BAA2B;IAC3B,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAEhE,IAAI,YAAY,EAAE;YACjB,OAAO,IAAI,CAAC;SACZ;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4EAA4E;IAC5E,yEAAyE;IACzE,YAAY;IACZ,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,yEAAyE;IAExJ,uDAAuD;IACvD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QAChC,QAAQ,CAAC,SAAS,GAAG,cAAc;aACjC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;aACzD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAEhD,OAAO,QAAQ,CAAC;IACjB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/ircmessageparser/parseStyle.js b/client/dist/js/helpers/ircmessageparser/parseStyle.js
new file mode 100644
index 00000000..0b2faf54
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/parseStyle.js
@@ -0,0 +1,198 @@
+"use strict";
+// Styling control codes
+const BOLD = "\x02";
+const COLOR = "\x03";
+const HEX_COLOR = "\x04";
+const RESET = "\x0f";
+const REVERSE = "\x16";
+const ITALIC = "\x1d";
+const UNDERLINE = "\x1f";
+const STRIKETHROUGH = "\x1e";
+const MONOSPACE = "\x11";
+// Color code matcher, with format `XX,YY` where both `XX` and `YY` are
+// integers, `XX` is the text color and `YY` is an optional background color.
+const colorRx = /^(\d{1,2})(?:,(\d{1,2}))?/;
+// 6-char Hex color code matcher
+const hexColorRx = /^([0-9a-f]{6})(?:,([0-9a-f]{6}))?/i;
+// Represents all other control codes that to be ignored/filtered from the text
+// This regex allows line feed character
+const controlCodesRx = /[\u0000-\u0009\u000B-\u001F]/g;
+// Converts a given text into an array of objects, each of them representing a
+// similarly styled section of the text. Each object carries the `text`, style
+// information (`bold`, `textColor`, `bgcolor`, `italic`,
+// `underline`, `strikethrough`, `monospace`), and `start`/`end` cursors.
+function parseStyle(text) {
+ const result = [];
+ let start = 0;
+ let position = 0;
+ // At any given time, these carry style information since last time a styling
+ // control code was met.
+ let colorCodes,
+ bold,
+ textColor,
+ bgColor,
+ hexColor,
+ hexBgColor,
+ italic,
+ underline,
+ strikethrough,
+ monospace;
+ const resetStyle = () => {
+ bold = false;
+ textColor = undefined;
+ bgColor = undefined;
+ hexColor = undefined;
+ hexBgColor = undefined;
+ italic = false;
+ underline = false;
+ strikethrough = false;
+ monospace = false;
+ };
+ resetStyle();
+ // When called, this "closes" the current fragment by adding an entry to the
+ // `result` array using the styling information set last time a control code
+ // was met.
+ const emitFragment = () => {
+ // Uses the text fragment starting from the last control code position up to
+ // the current position
+ const textPart = text.slice(start, position);
+ // Filters out all non-style related control codes present in this text
+ const processedText = textPart.replace(controlCodesRx, " ");
+ if (processedText.length) {
+ // Current fragment starts where the previous one ends, or at 0 if none
+ const fragmentStart = result.length ? result[result.length - 1].end : 0;
+ result.push({
+ bold,
+ textColor,
+ bgColor,
+ hexColor,
+ hexBgColor,
+ italic,
+ underline,
+ strikethrough,
+ monospace,
+ text: processedText,
+ start: fragmentStart,
+ end: fragmentStart + processedText.length,
+ });
+ }
+ // Now that a fragment has been "closed", the next one will start after that
+ start = position + 1;
+ };
+ // This loop goes through each character of the given text one by one by
+ // bumping the `position` cursor. Every time a new special "styling" character
+ // is met, an object gets created (with `emitFragment()`)information on text
+ // encountered since the previous styling character.
+ while (position < text.length) {
+ switch (text[position]) {
+ case RESET:
+ emitFragment();
+ resetStyle();
+ break;
+ // Meeting a BOLD character means that the ongoing text is either going to
+ // be in bold or that the previous one was in bold and the following one
+ // must be reset.
+ // This same behavior applies to COLOR, REVERSE, ITALIC, and UNDERLINE.
+ case BOLD:
+ emitFragment();
+ bold = !bold;
+ break;
+ case COLOR:
+ emitFragment();
+ // Go one step further to find the corresponding color
+ colorCodes = text.slice(position + 1).match(colorRx);
+ if (colorCodes) {
+ textColor = Number(colorCodes[1]);
+ if (colorCodes[2]) {
+ bgColor = Number(colorCodes[2]);
+ }
+ // Color code length is > 1, so bump the current position cursor by as
+ // much (and reset the start cursor for the current text block as well)
+ position += colorCodes[0].length;
+ start = position + 1;
+ } else {
+ // If no color codes were found, toggles back to no colors (like BOLD).
+ textColor = undefined;
+ bgColor = undefined;
+ }
+ break;
+ case HEX_COLOR:
+ emitFragment();
+ colorCodes = text.slice(position + 1).match(hexColorRx);
+ if (colorCodes) {
+ hexColor = colorCodes[1].toUpperCase();
+ if (colorCodes[2]) {
+ hexBgColor = colorCodes[2].toUpperCase();
+ }
+ // Color code length is > 1, so bump the current position cursor by as
+ // much (and reset the start cursor for the current text block as well)
+ position += colorCodes[0].length;
+ start = position + 1;
+ } else {
+ // If no color codes were found, toggles back to no colors (like BOLD).
+ hexColor = undefined;
+ hexBgColor = undefined;
+ }
+ break;
+ case REVERSE: {
+ emitFragment();
+ const tmp = bgColor;
+ bgColor = textColor;
+ textColor = tmp;
+ break;
+ }
+ case ITALIC:
+ emitFragment();
+ italic = !italic;
+ break;
+ case UNDERLINE:
+ emitFragment();
+ underline = !underline;
+ break;
+ case STRIKETHROUGH:
+ emitFragment();
+ strikethrough = !strikethrough;
+ break;
+ case MONOSPACE:
+ emitFragment();
+ monospace = !monospace;
+ break;
+ }
+ // Evaluate the next character at the next iteration
+ position += 1;
+ }
+ // The entire text has been parsed, so we finalize the current text fragment.
+ emitFragment();
+ return result;
+}
+const properties = [
+ "bold",
+ "textColor",
+ "bgColor",
+ "hexColor",
+ "hexBgColor",
+ "italic",
+ "underline",
+ "strikethrough",
+ "monospace",
+];
+function prepare(text) {
+ return (
+ parseStyle(text)
+ // This optimizes fragments by combining them together when all their values
+ // for the properties defined above are equal.
+ .reduce((prev, curr) => {
+ if (prev.length) {
+ const lastEntry = prev[prev.length - 1];
+ if (properties.every((key) => curr[key] === lastEntry[key])) {
+ lastEntry.text += curr.text;
+ lastEntry.end += curr.text.length;
+ return prev;
+ }
+ }
+ return prev.concat([curr]);
+ }, [])
+ );
+}
+export default prepare;
+//# sourceMappingURL=parseStyle.js.map
diff --git a/client/dist/js/helpers/ircmessageparser/parseStyle.js.map b/client/dist/js/helpers/ircmessageparser/parseStyle.js.map
new file mode 100644
index 00000000..92b24a6d
--- /dev/null
+++ b/client/dist/js/helpers/ircmessageparser/parseStyle.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"parseStyle.js","sourceRoot":"","sources":["../../../../js/helpers/ircmessageparser/parseStyle.js"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/isChannelCollapsed.js b/client/dist/js/helpers/isChannelCollapsed.js
new file mode 100644
index 00000000..d0b6d3ff
--- /dev/null
+++ b/client/dist/js/helpers/isChannelCollapsed.js
@@ -0,0 +1,12 @@
+"use strict";
+import store from "../store";
+export default (network, channel) => {
+ if (!network.isCollapsed || channel.highlight || channel.type === "lobby") {
+ return false;
+ }
+ if (store.state.activeChannel && channel === store.state.activeChannel.channel) {
+ return false;
+ }
+ return true;
+};
+//# sourceMappingURL=isChannelCollapsed.js.map
diff --git a/client/dist/js/helpers/isChannelCollapsed.js.map b/client/dist/js/helpers/isChannelCollapsed.js.map
new file mode 100644
index 00000000..b56884ca
--- /dev/null
+++ b/client/dist/js/helpers/isChannelCollapsed.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"isChannelCollapsed.js","sourceRoot":"","sources":["../../../js/helpers/isChannelCollapsed.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,MAAM,UAAU,CAAC;AAE7B,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;IACnC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE;QAC1E,OAAO,KAAK,CAAC;KACb;IAED,IAAI,KAAK,CAAC,KAAK,CAAC,aAAa,IAAI,OAAO,KAAK,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE;QAC/E,OAAO,KAAK,CAAC;KACb;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/isIgnoredKeybind.js b/client/dist/js/helpers/isIgnoredKeybind.js
new file mode 100644
index 00000000..47e710e8
--- /dev/null
+++ b/client/dist/js/helpers/isIgnoredKeybind.js
@@ -0,0 +1,11 @@
+"use strict";
+export default (event) => {
+ if (event.target.tagName !== "TEXTAREA" && event.target.tagName !== "INPUT") {
+ return false;
+ }
+ // If focus is in a textarea, do not handle keybinds if user has typed anything
+ // This is done to prevent keyboard layout binds conflicting with ours
+ // For example alt+shift+left on macos selects a word
+ return !!event.target.value;
+};
+//# sourceMappingURL=isIgnoredKeybind.js.map
diff --git a/client/dist/js/helpers/isIgnoredKeybind.js.map b/client/dist/js/helpers/isIgnoredKeybind.js.map
new file mode 100644
index 00000000..1a66dd7d
--- /dev/null
+++ b/client/dist/js/helpers/isIgnoredKeybind.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"isIgnoredKeybind.js","sourceRoot":"","sources":["../../../js/helpers/isIgnoredKeybind.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,eAAe,CAAC,KAAK,EAAE,EAAE;IACxB,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE;QAC5E,OAAO,KAAK,CAAC;KACb;IAED,+EAA+E;IAC/E,sEAAsE;IACtE,qDAAqD;IACrD,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;AAC7B,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/listenForTwoFingerSwipes.js b/client/dist/js/helpers/listenForTwoFingerSwipes.js
new file mode 100644
index 00000000..249fb96a
--- /dev/null
+++ b/client/dist/js/helpers/listenForTwoFingerSwipes.js
@@ -0,0 +1,85 @@
+"use strict";
+import distance from "./distance";
+// onTwoFingerSwipe will be called with a cardinal direction ("n", "e", "s" or
+// "w") as its only argument.
+function listenForTwoFingerSwipes(onTwoFingerSwipe) {
+ let history = [];
+ document.body.addEventListener(
+ "touchmove",
+ function (event) {
+ if (event.touches.length !== 2) {
+ return;
+ }
+ const a = event.touches.item(0);
+ const b = event.touches.item(1);
+ const timestamp = window.performance.now();
+ const center = [(a.screenX + b.screenX) / 2, (a.screenY + b.screenY) / 2];
+ if (history.length > 0) {
+ const last = history[history.length - 1];
+ const centersAreEqual =
+ last.center[0] === center[0] && last.center[1] === center[1];
+ if (last.timestamp === timestamp || centersAreEqual) {
+ // Touches with the same timestamps or center don't help us
+ // see the speed of movement. Ignore them.
+ return;
+ }
+ }
+ history.push({timestamp, center});
+ },
+ {passive: true}
+ );
+ document.body.addEventListener(
+ "touchend",
+ function (event) {
+ if (event.touches.length >= 2) {
+ return;
+ }
+ try {
+ const direction = getSwipe(history);
+ if (direction) {
+ onTwoFingerSwipe(direction);
+ }
+ } finally {
+ history = [];
+ }
+ },
+ {passive: true}
+ );
+ document.body.addEventListener(
+ "touchcancel",
+ function () {
+ history = [];
+ },
+ {passive: true}
+ );
+}
+// Returns the cardinal direction of the swipe or null if there is no swipe.
+function getSwipe(hist) {
+ // Speed is in pixels/millisecond. Must be maintained throughout swipe.
+ const MIN_SWIPE_SPEED = 0.2;
+ if (hist.length < 2) {
+ return null;
+ }
+ for (let i = 1; i < hist.length; ++i) {
+ const previous = hist[i - 1];
+ const current = hist[i];
+ const speed =
+ distance(previous.center, current.center) /
+ Math.abs(previous.timestamp - current.timestamp);
+ if (speed < MIN_SWIPE_SPEED) {
+ return null;
+ }
+ }
+ return getCardinalDirection(hist[0].center, hist[hist.length - 1].center);
+}
+function getCardinalDirection([x1, y1], [x2, y2]) {
+ // If θ is the angle of the vector then this is tan(θ)
+ const tangent = (y2 - y1) / (x2 - x1);
+ // All values of |tan(-45° to 45°)| are less than 1, same for 145° to 225°
+ if (Math.abs(tangent) < 1) {
+ return x1 < x2 ? "e" : "w";
+ }
+ return y1 < y2 ? "s" : "n";
+}
+export default listenForTwoFingerSwipes;
+//# sourceMappingURL=listenForTwoFingerSwipes.js.map
diff --git a/client/dist/js/helpers/listenForTwoFingerSwipes.js.map b/client/dist/js/helpers/listenForTwoFingerSwipes.js.map
new file mode 100644
index 00000000..44adb07b
--- /dev/null
+++ b/client/dist/js/helpers/listenForTwoFingerSwipes.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"listenForTwoFingerSwipes.js","sourceRoot":"","sources":["../../../js/helpers/listenForTwoFingerSwipes.js"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/localetime.js b/client/dist/js/helpers/localetime.js
new file mode 100644
index 00000000..a42f3f8d
--- /dev/null
+++ b/client/dist/js/helpers/localetime.js
@@ -0,0 +1,4 @@
+"use strict";
+import dayjs from "dayjs";
+export default (time) => dayjs(time).format("D MMMM YYYY, HH:mm:ss");
+//# sourceMappingURL=localetime.js.map
diff --git a/client/dist/js/helpers/localetime.js.map b/client/dist/js/helpers/localetime.js.map
new file mode 100644
index 00000000..8d3e38b1
--- /dev/null
+++ b/client/dist/js/helpers/localetime.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"localetime.js","sourceRoot":"","sources":["../../../js/helpers/localetime.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/parse.js b/client/dist/js/helpers/parse.js
new file mode 100644
index 00000000..86a7e178
--- /dev/null
+++ b/client/dist/js/helpers/parse.js
@@ -0,0 +1,177 @@
+"use strict";
+import parseStyle from "./ircmessageparser/parseStyle";
+import findChannels from "./ircmessageparser/findChannels";
+import {findLinks} from "./ircmessageparser/findLinks";
+import findEmoji from "./ircmessageparser/findEmoji";
+import findNames from "./ircmessageparser/findNames";
+import merge from "./ircmessageparser/merge";
+import emojiMap from "./fullnamemap.json";
+import LinkPreviewToggle from "../../components/LinkPreviewToggle.vue";
+import LinkPreviewFileSize from "../../components/LinkPreviewFileSize.vue";
+import InlineChannel from "../../components/InlineChannel.vue";
+import Username from "../../components/Username.vue";
+const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]|\u{fe0f}/gu;
+// Create an HTML `span` with styling information for a given fragment
+function createFragment(fragment, createElement) {
+ const classes = [];
+ if (fragment.bold) {
+ classes.push("irc-bold");
+ }
+ if (fragment.textColor !== undefined) {
+ classes.push("irc-fg" + fragment.textColor);
+ }
+ if (fragment.bgColor !== undefined) {
+ classes.push("irc-bg" + fragment.bgColor);
+ }
+ if (fragment.italic) {
+ classes.push("irc-italic");
+ }
+ if (fragment.underline) {
+ classes.push("irc-underline");
+ }
+ if (fragment.strikethrough) {
+ classes.push("irc-strikethrough");
+ }
+ if (fragment.monospace) {
+ classes.push("irc-monospace");
+ }
+ const data = {};
+ let hasData = false;
+ if (classes.length > 0) {
+ hasData = true;
+ data.class = classes;
+ }
+ if (fragment.hexColor) {
+ hasData = true;
+ data.style = {
+ color: `#${fragment.hexColor}`,
+ };
+ if (fragment.hexBgColor) {
+ data.style["background-color"] = `#${fragment.hexBgColor}`;
+ }
+ }
+ return hasData ? createElement("span", data, fragment.text) : fragment.text;
+}
+// Transform an IRC message potentially filled with styling control codes, URLs,
+// nicknames, and channels into a string of HTML elements to display on the client.
+function parse(createElement, text, message = undefined, network = undefined) {
+ // Extract the styling information and get the plain text version from it
+ const styleFragments = parseStyle(text);
+ const cleanText = styleFragments.map((fragment) => fragment.text).join("");
+ // On the plain text, find channels and URLs, returned as "parts". Parts are
+ // arrays of objects containing start and end markers, as well as metadata
+ // depending on what was found (channel or link).
+ const channelPrefixes = network ? network.serverOptions.CHANTYPES : ["#", "&"];
+ const userModes = network?.serverOptions?.PREFIX.symbols || ["!", "@", "%", "+"];
+ const channelParts = findChannels(cleanText, channelPrefixes, userModes);
+ const linkParts = findLinks(cleanText);
+ const emojiParts = findEmoji(cleanText);
+ const nameParts = findNames(cleanText, message ? message.users || [] : []);
+ const parts = channelParts.concat(linkParts).concat(emojiParts).concat(nameParts);
+ // The channel the message belongs to might not exist if the user isn't joined to it.
+ const messageChannel = message ? message.channel : null;
+ // Merge the styling information with the channels / URLs / nicks / text objects and
+ // generate HTML strings with the resulting fragments
+ return merge(parts, styleFragments, cleanText).map((textPart) => {
+ const fragments = textPart.fragments.map((fragment) =>
+ createFragment(fragment, createElement)
+ );
+ // Wrap these potentially styled fragments with links and channel buttons
+ if (textPart.link) {
+ const preview =
+ message &&
+ message.previews &&
+ message.previews.find((p) => p.link === textPart.link);
+ const link = createElement(
+ "a",
+ {
+ attrs: {
+ href: textPart.link,
+ dir: preview ? null : "auto",
+ target: "_blank",
+ rel: "noopener",
+ },
+ },
+ fragments
+ );
+ if (!preview) {
+ return link;
+ }
+ const linkEls = [link];
+ if (preview.size > 0) {
+ linkEls.push(
+ createElement(LinkPreviewFileSize, {
+ props: {
+ size: preview.size,
+ },
+ })
+ );
+ }
+ linkEls.push(
+ createElement(LinkPreviewToggle, {
+ props: {
+ link: preview,
+ },
+ })
+ );
+ // We wrap the link, size, and the toggle button into
+ // to correctly keep the left-to-right order of these elements
+ return createElement(
+ "span",
+ {
+ attrs: {
+ dir: "auto",
+ },
+ },
+ linkEls
+ );
+ } else if (textPart.channel) {
+ return createElement(
+ InlineChannel,
+ {
+ props: {
+ channel: textPart.channel,
+ },
+ },
+ fragments
+ );
+ } else if (textPart.emoji) {
+ const emojiWithoutModifiers = textPart.emoji.replace(emojiModifiersRegex, "");
+ const title = emojiMap[emojiWithoutModifiers]
+ ? `Emoji: ${emojiMap[emojiWithoutModifiers]}`
+ : null;
+ return createElement(
+ "span",
+ {
+ class: ["emoji"],
+ attrs: {
+ role: "img",
+ "aria-label": title,
+ title: title,
+ },
+ },
+ fragments
+ );
+ } else if (textPart.nick) {
+ return createElement(
+ Username,
+ {
+ props: {
+ user: {
+ nick: textPart.nick,
+ },
+ channel: messageChannel,
+ network,
+ },
+ attrs: {
+ dir: "auto",
+ },
+ },
+ fragments
+ );
+ }
+ return fragments;
+ });
+}
+export default parse;
+//# sourceMappingURL=parse.js.map
diff --git a/client/dist/js/helpers/parse.js.map b/client/dist/js/helpers/parse.js.map
new file mode 100644
index 00000000..66912fa8
--- /dev/null
+++ b/client/dist/js/helpers/parse.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../js/helpers/parse.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,UAAU,MAAM,+BAA+B,CAAC;AACvD,OAAO,YAAY,MAAM,iCAAiC,CAAC;AAC3D,OAAO,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AACvD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,KAAK,MAAM,0BAA0B,CAAC;AAC7C,OAAO,QAAQ,MAAM,oBAAoB,CAAC;AAC1C,OAAO,iBAAiB,MAAM,wCAAwC,CAAC;AACvE,OAAO,mBAAmB,MAAM,0CAA0C,CAAC;AAC3E,OAAO,aAAa,MAAM,oCAAoC,CAAC;AAC/D,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AAErD,MAAM,mBAAmB,GAAG,kCAAkC,CAAC;AAE/D,sEAAsE;AACtE,SAAS,cAAc,CAAC,QAAQ,EAAE,aAAa;IAC9C,MAAM,OAAO,GAAG,EAAE,CAAC;IAEnB,IAAI,QAAQ,CAAC,IAAI,EAAE;QAClB,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;KACzB;IAED,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;KAC5C;IAED,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE;QACnC,OAAO,CAAC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;KAC1C;IAED,IAAI,QAAQ,CAAC,MAAM,EAAE;QACpB,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;KAC3B;IAED,IAAI,QAAQ,CAAC,SAAS,EAAE;QACvB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;KAC9B;IAED,IAAI,QAAQ,CAAC,aAAa,EAAE;QAC3B,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;KAClC;IAED,IAAI,QAAQ,CAAC,SAAS,EAAE;QACvB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;KAC9B;IAED,MAAM,IAAI,GAAG,EAAE,CAAC;IAChB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;QACvB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;KACrB;IAED,IAAI,QAAQ,CAAC,QAAQ,EAAE;QACtB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,KAAK,GAAG;YACZ,KAAK,EAAE,IAAI,QAAQ,CAAC,QAAQ,EAAE;SAC9B,CAAC;QAEF,IAAI,QAAQ,CAAC,UAAU,EAAE;YACxB,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;SAC3D;KACD;IAED,OAAO,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC7E,CAAC;AAED,gFAAgF;AAChF,mFAAmF;AACnF,SAAS,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,GAAG,SAAS;IAC3E,yEAAyE;IACzE,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE3E,4EAA4E;IAC5E,0EAA0E;IAC1E,iDAAiD;IACjD,MAAM,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACjF,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE3E,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAElF,qFAAqF;IACrF,MAAM,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAExD,oFAAoF;IACpF,qDAAqD;IACrD,OAAO,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC/D,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACrD,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CACvC,CAAC;QAEF,yEAAyE;QACzE,IAAI,QAAQ,CAAC,IAAI,EAAE;YAClB,MAAM,OAAO,GACZ,OAAO;gBACP,OAAO,CAAC,QAAQ;gBAChB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,aAAa,CACzB,GAAG,EACH;gBACC,KAAK,EAAE;oBACN,IAAI,EAAE,QAAQ,CAAC,IAAI;oBACnB,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;oBAC5B,MAAM,EAAE,QAAQ;oBAChB,GAAG,EAAE,UAAU;iBACf;aACD,EACD,SAAS,CACT,CAAC;YAEF,IAAI,CAAC,OAAO,EAAE;gBACb,OAAO,IAAI,CAAC;aACZ;YAED,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;YAEvB,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE;gBACrB,OAAO,CAAC,IAAI,CACX,aAAa,CAAC,mBAAmB,EAAE;oBAClC,KAAK,EAAE;wBACN,IAAI,EAAE,OAAO,CAAC,IAAI;qBAClB;iBACD,CAAC,CACF,CAAC;aACF;YAED,OAAO,CAAC,IAAI,CACX,aAAa,CAAC,iBAAiB,EAAE;gBAChC,KAAK,EAAE;oBACN,IAAI,EAAE,OAAO;iBACb;aACD,CAAC,CACF,CAAC;YAEF,uEAAuE;YACvE,8DAA8D;YAC9D,OAAO,aAAa,CACnB,MAAM,EACN;gBACC,KAAK,EAAE;oBACN,GAAG,EAAE,MAAM;iBACX;aACD,EACD,OAAO,CACP,CAAC;SACF;aAAM,IAAI,QAAQ,CAAC,OAAO,EAAE;YAC5B,OAAO,aAAa,CACnB,aAAa,EACb;gBACC,KAAK,EAAE;oBACN,OAAO,EAAE,QAAQ,CAAC,OAAO;iBACzB;aACD,EACD,SAAS,CACT,CAAC;SACF;aAAM,IAAI,QAAQ,CAAC,KAAK,EAAE;YAC1B,MAAM,qBAAqB,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;YAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,qBAAqB,CAAC;gBAC5C,CAAC,CAAC,UAAU,QAAQ,CAAC,qBAAqB,CAAC,EAAE;gBAC7C,CAAC,CAAC,IAAI,CAAC;YAER,OAAO,aAAa,CACnB,MAAM,EACN;gBACC,KAAK,EAAE,CAAC,OAAO,CAAC;gBAChB,KAAK,EAAE;oBACN,IAAI,EAAE,KAAK;oBACX,YAAY,EAAE,KAAK;oBACnB,KAAK,EAAE,KAAK;iBACZ;aACD,EACD,SAAS,CACT,CAAC;SACF;aAAM,IAAI,QAAQ,CAAC,IAAI,EAAE;YACzB,OAAO,aAAa,CACnB,QAAQ,EACR;gBACC,KAAK,EAAE;oBACN,IAAI,EAAE;wBACL,IAAI,EAAE,QAAQ,CAAC,IAAI;qBACnB;oBACD,OAAO,EAAE,cAAc;oBACvB,OAAO;iBACP;gBACD,KAAK,EAAE;oBACN,GAAG,EAAE,MAAM;iBACX;aACD,EACD,SAAS,CACT,CAAC;SACF;QAED,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/parseIrcUri.js b/client/dist/js/helpers/parseIrcUri.js
new file mode 100644
index 00000000..909eacb1
--- /dev/null
+++ b/client/dist/js/helpers/parseIrcUri.js
@@ -0,0 +1,43 @@
+"use strict";
+export default (stringUri) => {
+ const data = {};
+ try {
+ // https://tools.ietf.org/html/draft-butcher-irc-url-04
+ const uri = new URL(stringUri);
+ // Replace protocol with a "special protocol" (that's what it's called in WHATWG spec)
+ // So that the uri can be properly parsed
+ if (uri.protocol === "irc:") {
+ uri.protocol = "http:";
+ if (!uri.port) {
+ uri.port = 6667;
+ }
+ data.tls = false;
+ } else if (uri.protocol === "ircs:") {
+ uri.protocol = "https:";
+ if (!uri.port) {
+ uri.port = 6697;
+ }
+ data.tls = true;
+ } else {
+ return;
+ }
+ if (!uri.hostname) {
+ return {};
+ }
+ data.host = data.name = uri.hostname;
+ data.port = uri.port;
+ let channel = "";
+ if (uri.pathname.length > 1) {
+ channel = uri.pathname.substr(1); // Remove slash
+ }
+ if (uri.hash.length > 1) {
+ channel += uri.hash;
+ }
+ // We don't split channels or append # here because the connect window takes care of that
+ data.join = channel;
+ } catch (e) {
+ // do nothing on invalid uri
+ }
+ return data;
+};
+//# sourceMappingURL=parseIrcUri.js.map
diff --git a/client/dist/js/helpers/parseIrcUri.js.map b/client/dist/js/helpers/parseIrcUri.js.map
new file mode 100644
index 00000000..8735220a
--- /dev/null
+++ b/client/dist/js/helpers/parseIrcUri.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"parseIrcUri.js","sourceRoot":"","sources":["../../../js/helpers/parseIrcUri.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,eAAe,CAAC,SAAS,EAAE,EAAE;IAC5B,MAAM,IAAI,GAAG,EAAE,CAAC;IAEhB,IAAI;QACH,uDAAuD;QACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QAE/B,sFAAsF;QACtF,yCAAyC;QACzC,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE;YAC5B,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC;YAEvB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;gBACd,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;aAChB;YAED,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;SACjB;aAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE;YACpC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAExB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;gBACd,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;aAChB;YAED,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;SAChB;aAAM;YACN,OAAO;SACP;QAED,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE;YAClB,OAAO,EAAE,CAAC;SACV;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QAErB,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YAC5B,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe;SACjD;QAED,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;YACxB,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC;SACpB;QAED,yFAAyF;QACzF,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;KACpB;IAAC,OAAO,CAAC,EAAE;QACX,4BAA4B;KAC5B;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/helpers/roundBadgeNumber.js b/client/dist/js/helpers/roundBadgeNumber.js
new file mode 100644
index 00000000..d8050c60
--- /dev/null
+++ b/client/dist/js/helpers/roundBadgeNumber.js
@@ -0,0 +1,8 @@
+"use strict";
+export default (count) => {
+ if (count < 1000) {
+ return count.toString();
+ }
+ return (count / 1000).toFixed(2).slice(0, -1) + "k";
+};
+//# sourceMappingURL=roundBadgeNumber.js.map
diff --git a/client/dist/js/helpers/roundBadgeNumber.js.map b/client/dist/js/helpers/roundBadgeNumber.js.map
new file mode 100644
index 00000000..3c77c631
--- /dev/null
+++ b/client/dist/js/helpers/roundBadgeNumber.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"roundBadgeNumber.js","sourceRoot":"","sources":["../../../js/helpers/roundBadgeNumber.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,eAAe,CAAC,KAAK,EAAE,EAAE;IACxB,IAAI,KAAK,GAAG,IAAI,EAAE;QACjB,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;KACxB;IAED,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AACrD,CAAC,CAAC"}
\ No newline at end of file
diff --git a/client/dist/js/keybinds.js b/client/dist/js/keybinds.js
new file mode 100644
index 00000000..7263ffd7
--- /dev/null
+++ b/client/dist/js/keybinds.js
@@ -0,0 +1,188 @@
+"use strict";
+import Mousetrap from "mousetrap";
+import store from "./store";
+import {switchToChannel, router, navigate} from "./router";
+import isChannelCollapsed from "./helpers/isChannelCollapsed";
+import isIgnoredKeybind from "./helpers/isIgnoredKeybind";
+import listenForTwoFingerSwipes from "./helpers/listenForTwoFingerSwipes";
+// Switch to the next/previous window in the channel list.
+Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) {
+ if (isIgnoredKeybind(e)) {
+ return true;
+ }
+ navigateWindow(keys.split("+").pop() === "up" ? -1 : 1);
+ return false;
+});
+listenForTwoFingerSwipes(function (cardinalDirection) {
+ if (cardinalDirection === "e" || cardinalDirection === "w") {
+ navigateWindow(cardinalDirection === "e" ? -1 : 1);
+ }
+});
+function navigateWindow(direction) {
+ if (store.state.networks.length === 0) {
+ return;
+ }
+ const flatChannels = [];
+ let index = -1;
+ for (const network of store.state.networks) {
+ for (const channel of network.channels) {
+ if (isChannelCollapsed(network, channel)) {
+ continue;
+ }
+ if (
+ index === -1 &&
+ store.state.activeChannel &&
+ store.state.activeChannel.channel === channel
+ ) {
+ index = flatChannels.length;
+ }
+ flatChannels.push(channel);
+ }
+ }
+ // Circular array, and a modulo bug workaround because in JS it stays negative
+ const length = flatChannels.length;
+ index = (((index + direction) % length) + length) % length;
+ jumpToChannel(flatChannels[index]);
+}
+// Switch to the next/previous lobby in the channel list
+Mousetrap.bind(["alt+shift+up", "alt+shift+down"], function (e, keys) {
+ if (isIgnoredKeybind(e)) {
+ return true;
+ }
+ const length = store.state.networks.length;
+ if (length === 0) {
+ return false;
+ }
+ const direction = keys.split("+").pop() === "up" ? -1 : 1;
+ let index = 0;
+ // If we're in another window, jump to first lobby
+ if (store.state.activeChannel) {
+ index = store.state.networks.findIndex((n) => n === store.state.activeChannel.network);
+ // If we're in a channel, and it's not the lobby, jump to lobby of this network when going up
+ if (direction !== -1 || store.state.activeChannel.channel.type === "lobby") {
+ index = (((index + direction) % length) + length) % length;
+ }
+ }
+ jumpToChannel(store.state.networks[index].channels[0]);
+ return false;
+});
+// Jump to the first window with a highlight in it, or the first with unread
+// activity if there are none with highlights.
+Mousetrap.bind(["alt+a"], function (e) {
+ if (isIgnoredKeybind(e)) {
+ return true;
+ }
+ let targetChannel;
+ outer_loop: for (const network of store.state.networks) {
+ for (const chan of network.channels) {
+ if (chan.highlight) {
+ targetChannel = chan;
+ break outer_loop;
+ }
+ if (chan.unread && !targetChannel) {
+ targetChannel = chan;
+ }
+ }
+ }
+ if (targetChannel) {
+ jumpToChannel(targetChannel);
+ }
+ return false;
+});
+// Show the help menu.
+Mousetrap.bind(["alt+/"], function (e) {
+ if (isIgnoredKeybind(e)) {
+ return true;
+ }
+ navigate("Help");
+ return false;
+});
+function jumpToChannel(targetChannel) {
+ switchToChannel(targetChannel);
+ const element = document.querySelector(
+ `#sidebar .channel-list-item[aria-controls="#chan-${targetChannel.id}"]`
+ );
+ if (element) {
+ scrollIntoViewNicely(element);
+ }
+}
+// Ignored keys which should not automatically focus the input bar
+const ignoredKeys = {
+ 8: true,
+ 9: true,
+ 12: true,
+ 16: true,
+ 17: true,
+ 18: true,
+ 19: true,
+ 20: true,
+ 27: true,
+ 35: true,
+ 36: true,
+ 37: true,
+ 38: true,
+ 39: true,
+ 40: true,
+ 45: true,
+ 46: true,
+ 112: true,
+ 113: true,
+ 114: true,
+ 115: true,
+ 116: true,
+ 117: true,
+ 118: true,
+ 119: true,
+ 120: true,
+ 121: true,
+ 122: true,
+ 123: true,
+ 144: true,
+ 145: true,
+ 224: true, // Meta
+};
+document.addEventListener("keydown", (e) => {
+ // Allow navigating back to the previous page when on the help screen.
+ if (e.key === "Escape" && router.currentRoute.name === "Help") {
+ router.go(-1);
+ return;
+ }
+ // Ignore any key that uses alt modifier
+ // Ignore keys defined above
+ if (e.altKey || ignoredKeys[e.which]) {
+ return;
+ }
+ // Ignore all ctrl keys except for ctrl+v to allow pasting
+ if ((e.ctrlKey || e.metaKey) && e.which !== 86) {
+ return;
+ }
+ // Redirect pagedown/pageup keys to messages container so it scrolls
+ if (e.which === 33 || e.which === 34) {
+ const chat = document.querySelector(".window .chat-content .chat");
+ if (chat) {
+ chat.focus();
+ }
+ return;
+ }
+ const tagName = e.target.tagName;
+ // Ignore if we're already typing into or