UNPKG

mobidev-thelounge

Version:

The self-hosted Web IRC client

1,521 lines (1,242 loc) 1.35 MB
/******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ "./client/components/MessageTypes sync \\.vue$": /*!******************************************************************!*\ !*** ./client/components/MessageTypes/ sync nonrecursive \.vue$ ***! \******************************************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { var map = { "./away.vue": "./client/components/MessageTypes/away.vue", "./back.vue": "./client/components/MessageTypes/back.vue", "./chghost.vue": "./client/components/MessageTypes/chghost.vue", "./ctcp.vue": "./client/components/MessageTypes/ctcp.vue", "./ctcp_request.vue": "./client/components/MessageTypes/ctcp_request.vue", "./error.vue": "./client/components/MessageTypes/error.vue", "./invite.vue": "./client/components/MessageTypes/invite.vue", "./join.vue": "./client/components/MessageTypes/join.vue", "./kick.vue": "./client/components/MessageTypes/kick.vue", "./mc.vue": "./client/components/MessageTypes/mc.vue", "./mode.vue": "./client/components/MessageTypes/mode.vue", "./mode_channel.vue": "./client/components/MessageTypes/mode_channel.vue", "./mode_user.vue": "./client/components/MessageTypes/mode_user.vue", "./monospace_block.vue": "./client/components/MessageTypes/monospace_block.vue", "./nick.vue": "./client/components/MessageTypes/nick.vue", "./part.vue": "./client/components/MessageTypes/part.vue", "./quit.vue": "./client/components/MessageTypes/quit.vue", "./raw.vue": "./client/components/MessageTypes/raw.vue", "./topic.vue": "./client/components/MessageTypes/topic.vue", "./topic_set_by.vue": "./client/components/MessageTypes/topic_set_by.vue", "./whois.vue": "./client/components/MessageTypes/whois.vue" }; function webpackContext(req) { var id = webpackContextResolve(req); return __webpack_require__(id); } function webpackContextResolve(req) { if(!__webpack_require__.o(map, req)) { var e = new Error("Cannot find module '" + req + "'"); e.code = 'MODULE_NOT_FOUND'; throw e; } return map[req]; } webpackContext.keys = function webpackContextKeys() { return Object.keys(map); }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; webpackContext.id = "./client/components/MessageTypes sync \\.vue$"; /***/ }), /***/ "./client/js/commands sync recursive \\.ts$": /*!****************************************!*\ !*** ./client/js/commands/ sync \.ts$ ***! \****************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { var map = { "./collapse.ts": "./client/js/commands/collapse.ts", "./expand.ts": "./client/js/commands/expand.ts", "./index.ts": "./client/js/commands/index.ts", "./join.ts": "./client/js/commands/join.ts", "./search.ts": "./client/js/commands/search.ts" }; function webpackContext(req) { var id = webpackContextResolve(req); return __webpack_require__(id); } function webpackContextResolve(req) { if(!__webpack_require__.o(map, req)) { var e = new Error("Cannot find module '" + req + "'"); e.code = 'MODULE_NOT_FOUND'; throw e; } return map[req]; } webpackContext.keys = function webpackContextKeys() { return Object.keys(map); }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; webpackContext.id = "./client/js/commands sync recursive \\.ts$"; /***/ }), /***/ "./client/components/MessageTypes/index.ts": /*!*************************************************!*\ !*** ./client/components/MessageTypes/index.ts ***! \*************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // 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 = __webpack_require__("./client/components/MessageTypes sync \\.vue$"); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (requireViews.keys().reduce((acc, path) => { acc["message-" + path.substring(2, path.length - 4)] = requireViews(path).default; return acc; }, {})); /***/ }), /***/ "./client/js/auth.ts": /*!***************************!*\ !*** ./client/js/auth.ts ***! \***************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ Auth) /* harmony export */ }); /* harmony import */ var _localStorage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./localStorage */ "./client/js/localStorage.ts"); /* harmony import */ var _location__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./location */ "./client/js/location.ts"); class Auth { static signout() { _localStorage__WEBPACK_IMPORTED_MODULE_0__["default"].clear(); _location__WEBPACK_IMPORTED_MODULE_1__["default"].reload(); } } /***/ }), /***/ "./client/js/autocompletion.ts": /*!*************************************!*\ !*** ./client/js/autocompletion.ts ***! \*************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constants */ "./client/js/constants.ts"); /* harmony import */ var mousetrap__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! mousetrap */ "./node_modules/mousetrap/mousetrap.js"); /* harmony import */ var mousetrap__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(mousetrap__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _textcomplete_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @textcomplete/core */ "./node_modules/@textcomplete/core/dist/index.js"); /* harmony import */ var _textcomplete_core__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_textcomplete_core__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _textcomplete_textarea__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @textcomplete/textarea */ "./node_modules/@textcomplete/textarea/dist/index.js"); /* harmony import */ var fuzzy__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! fuzzy */ "./node_modules/fuzzy/lib/fuzzy.js"); /* harmony import */ var fuzzy__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(fuzzy__WEBPACK_IMPORTED_MODULE_4__); /* harmony import */ var _helpers_simplemap_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./helpers/simplemap.json */ "./client/js/helpers/simplemap.json"); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./store */ "./client/js/store.ts"); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (enableAutocomplete); const emojiSearchTerms = Object.keys(_helpers_simplemap_json__WEBPACK_IMPORTED_MODULE_5__); 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 `<span class="emoji">${String(_helpers_simplemap_json__WEBPACK_IMPORTED_MODULE_5__[original])}</span> ${string}`; }, replace([, original]) { return "$1" + String(_helpers_simplemap_json__WEBPACK_IMPORTED_MODULE_5__[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] === "@") { // TODO: type // eslint-disable-next-line @typescript-eslint/restrict-plus-operands 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__WEBPACK_IMPORTED_MODULE_0__["default"].colorCodeMap.filter(i => fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, i[0]) || fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, i[1])).map(i => { if (fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, i[1])) { return [i[0], fuzzy__WEBPACK_IMPORTED_MODULE_4___default().match(term, i[1], { pre: "<b>", post: "</b>" }).rendered]; } return i; }); callback(matchingColorCodes); }, template(value) { return `<span class="irc-fg${parseInt(value[0], 10)}">${value[1]}</span>`; }, 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__WEBPACK_IMPORTED_MODULE_0__["default"].colorCodeMap.filter(i => fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, i[0]) || fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, i[1])).map(pair => { if (fuzzy__WEBPACK_IMPORTED_MODULE_4___default().test(term, pair[1])) { return [pair[0], fuzzy__WEBPACK_IMPORTED_MODULE_4___default().match(term, pair[1], { pre: "<b>", post: "</b>" }).rendered]; } return pair; }).map(pair => pair.concat(match[1])); // Needed to pass fg color to `template`... callback(matchingColorCodes); }, template(value) { return `<span class="irc-fg${parseInt(value[2], 10)} irc-bg irc-bg${parseInt(value[0], 10)}">${value[1]}</span>`; }, 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__WEBPACK_IMPORTED_MODULE_1___default()(input).bind("tab", e => { if (_store__WEBPACK_IMPORTED_MODULE_6__.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( // TODO: type this properly String(currentMatches[tabCount % currentMatches.length]), position); const remainder = text.substring(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 _textcomplete_textarea__WEBPACK_IMPORTED_MODULE_3__.TextareaEditor(input); const textcomplete = new _textcomplete_core__WEBPACK_IMPORTED_MODULE_2__.Textcomplete(editor, strategies, { dropdown: { className: "textcomplete-menu", placement: "top" } }); textcomplete.on("show", () => { _store__WEBPACK_IMPORTED_MODULE_6__.store.commit("isAutoCompleting", true); }); textcomplete.on("hidden", () => { _store__WEBPACK_IMPORTED_MODULE_6__.store.commit("isAutoCompleting", false); }); return { hide() { textcomplete.hide(); }, destroy() { textcomplete.destroy(); _store__WEBPACK_IMPORTED_MODULE_6__.store.commit("isAutoCompleting", false); } }; } function replaceNick(original, position = 1) { // If no postfix specified, return autocompleted nick as-is if (!_store__WEBPACK_IMPORTED_MODULE_6__.store.state.settings.nickPostfix) { return original; } // If there is whitespace in the input already, append space to nick if (position > 0 && /\s/.test(_store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel.channel.pendingMessage || "")) { return original + " "; } // If nick is first in the input, append specified postfix return original + _store__WEBPACK_IMPORTED_MODULE_6__.store.state.settings.nickPostfix; } function fuzzyGrep(term, array) { const results = fuzzy__WEBPACK_IMPORTED_MODULE_4___default().filter(term, array, { pre: "<b>", post: "</b>" }); return results.map(el => [el.string, el.original]); } function rawNicks() { if (!_store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel) { return []; } if (_store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel.channel.users.length > 0) { const users = _store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel.channel.users.slice(); return users.sort((a, b) => b.lastMessage - a.lastMessage).map(u => u.nick); } const me = _store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel.network.nick; const otherUser = _store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel.channel.name; // If this is a query, add their name to autocomplete if (me !== otherUser && _store__WEBPACK_IMPORTED_MODULE_6__.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__WEBPACK_IMPORTED_MODULE_0__["default"].commands.slice(); if (!_store__WEBPACK_IMPORTED_MODULE_6__.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 = []; if (_store__WEBPACK_IMPORTED_MODULE_6__.store.state.activeChannel) { for (const channel of _store__WEBPACK_IMPORTED_MODULE_6__.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); } /***/ }), /***/ "./client/js/clipboard.ts": /*!********************************!*\ !*** ./client/js/clipboard.ts ***! \********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(chat) { // Disable in Firefox as it already copies flex text correctly // @ts-expect-error Property 'InstallTrigger' does not exist on type 'Window & typeof globalThis'.ts(2339) if (typeof window.InstallTrigger !== "undefined") { return; } const selection = window.getSelection(); if (!selection) { return; } // 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); } /***/ }), /***/ "./client/js/commands/collapse.ts": /*!****************************************!*\ !*** ./client/js/commands/collapse.ts ***! \****************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _socket__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../socket */ "./client/js/socket.ts"); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../store */ "./client/js/store.ts"); function input() { if (!_store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel) { return; } const messageIds = []; for (const message of _store__WEBPACK_IMPORTED_MODULE_1__.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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("msg:preview:toggle", { target: _store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.id, messageIds: messageIds, shown: false }); } return true; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ input }); /***/ }), /***/ "./client/js/commands/expand.ts": /*!**************************************!*\ !*** ./client/js/commands/expand.ts ***! \**************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _socket__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../socket */ "./client/js/socket.ts"); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../store */ "./client/js/store.ts"); function input() { if (!_store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel) { return; } const messageIds = []; for (const message of _store__WEBPACK_IMPORTED_MODULE_1__.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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("msg:preview:toggle", { target: _store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.id, messageIds: messageIds, shown: true }); } return true; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ input }); /***/ }), /***/ "./client/js/commands/index.ts": /*!*************************************!*\ !*** ./client/js/commands/index.ts ***! \*************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // 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 = __webpack_require__("./client/js/commands sync recursive \\.ts$"); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (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; }, {})); /***/ }), /***/ "./client/js/commands/join.ts": /*!************************************!*\ !*** ./client/js/commands/join.ts ***! \************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _socket__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../socket */ "./client/js/socket.ts"); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../store */ "./client/js/store.ts"); /* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../router */ "./client/js/router.ts"); function input(args) { if (args.length > 0) { let channels = args[0]; if (channels.length > 0) { const chanTypes = _store__WEBPACK_IMPORTED_MODULE_1__.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__WEBPACK_IMPORTED_MODULE_1__.store.getters.findChannelOnCurrentNetwork(channels); if (chan) { (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(chan); } else { if (_store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel) { _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { text: `/join ${channels} ${args.length > 1 ? args[1] : ""}`, target: _store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.id }); } return true; } } } else if (_store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.type === "channel") { // If `/join` command is used without any arguments, re-join current channel _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: _store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.id, text: `/join ${_store__WEBPACK_IMPORTED_MODULE_1__.store.state.activeChannel.channel.name}` }); return true; } } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ input }); /***/ }), /***/ "./client/js/commands/search.ts": /*!**************************************!*\ !*** ./client/js/commands/search.ts ***! \**************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../store */ "./client/js/store.ts"); /* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../router */ "./client/js/router.ts"); function input(args) { if (!_store__WEBPACK_IMPORTED_MODULE_0__.store.state.settings.searchEnabled) { return false; } _router__WEBPACK_IMPORTED_MODULE_1__.router.push({ name: "SearchResults", params: { id: _store__WEBPACK_IMPORTED_MODULE_0__.store.state.activeChannel.channel.id }, query: { q: args.join(" ") } }).catch(e => { // eslint-disable-next-line no-console console.error(`Failed to push SearchResults route: ${e.message}`); }); return true; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ input }); /***/ }), /***/ "./client/js/constants.ts": /*!********************************!*\ !*** ./client/js/constants.ts ***! \********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); 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" }; /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ colorCodeMap, commands: [], condensedTypes, timeFormats, // Same value as media query in CSS that forces sidebars to become overlays mobileViewportPixels: 768 }); /***/ }), /***/ "./client/js/eventbus.ts": /*!*******************************!*\ !*** ./client/js/eventbus.ts ***! \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); 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); }); } } } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (new EventBus()); /***/ }), /***/ "./client/js/helpers/collapseNetwork.ts": /*!**********************************************!*\ !*** ./client/js/helpers/collapseNetwork.ts ***! \**********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _localStorage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../localStorage */ "./client/js/localStorage.ts"); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((network, isCollapsed) => { const stored = _localStorage__WEBPACK_IMPORTED_MODULE_0__["default"].get("thelounge.networks.collapsed"); const networks = stored ? new Set(JSON.parse(stored)) : new Set(); network.isCollapsed = isCollapsed; if (isCollapsed) { networks.add(network.uuid); } else { networks.delete(network.uuid); } _localStorage__WEBPACK_IMPORTED_MODULE_0__["default"].set("thelounge.networks.collapsed", JSON.stringify([...networks])); }); /***/ }), /***/ "./client/js/helpers/colorClass.ts": /*!*****************************************!*\ !*** ./client/js/helpers/colorClass.ts ***! \*****************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // Generates a string from "color-1" to "color-32" based on an input string /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (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).toString(); }); /***/ }), /***/ "./client/js/helpers/contextMenu.ts": /*!******************************************!*\ !*** ./client/js/helpers/contextMenu.ts ***! \******************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "generateChannelContextMenu": () => (/* binding */ generateChannelContextMenu), /* harmony export */ "generateInlineChannelContextMenu": () => (/* binding */ generateInlineChannelContextMenu), /* harmony export */ "generateUserContextMenu": () => (/* binding */ generateUserContextMenu) /* harmony export */ }); /* harmony import */ var _socket__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../socket */ "./client/js/socket.ts"); /* harmony import */ var _eventbus__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../eventbus */ "./client/js/eventbus.ts"); /* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../router */ "./client/js/router.ts"); /* harmony import */ var _hooks_use_close_channel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../hooks/use-close-channel */ "./client/js/hooks/use-close-channel.ts"); function generateChannelContextMenu(channel, network) { const closeChannel = (0,_hooks_use_close_channel__WEBPACK_IMPORTED_MODULE_3__["default"])(channel); 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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/list" }) }, { label: "List ignored users", type: "item", class: "list", action: () => _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/ignorelist" }) }, network.status.connected ? { label: "Disconnect", type: "item", class: "disconnect", action: () => _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/disconnect" }) } : { label: "Connect", type: "item", class: "connect", action: () => _socket__WEBPACK_IMPORTED_MODULE_0__["default"].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; (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(channel); } }); items.push({ label: "List banned users", type: "item", class: "list", action() { _socket__WEBPACK_IMPORTED_MODULE_0__["default"].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() { (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(channel); _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/whois " + channel.name }); } }, { label: "Ignore user", type: "item", class: "action-ignore", action() { _socket__WEBPACK_IMPORTED_MODULE_0__["default"].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__WEBPACK_IMPORTED_MODULE_1__["default"].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__WEBPACK_IMPORTED_MODULE_0__["default"].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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("mute:change", { target: channel.id, setMutedTo: !channel.muted }); } }); } // Add close menu item items.push({ label: closeMap[channel.type], type: "item", class: "close", action() { closeChannel(); } }); return items; } function generateInlineChannelContextMenu(store, chan, network) { const join = () => { const channel = network.channels.find(c => c.name === chan); if (channel) { (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(channel); } if (store.state.activeChannel) { _socket__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: store.state.activeChannel.channel.id, text: "/join " + chan }); } else { // eslint-disable-next-line no-console console.error("Unable to join channel: activeChannel is undefined"); } }; 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 }]; } function generateUserContextMenu(store, 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) { (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(chan); } _socket__WEBPACK_IMPORTED_MODULE_0__["default"].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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/ignore " + user.nick }); } }, { label: "Direct messages", type: "item", class: "action-query", action() { const chan = store.getters.findChannelOnCurrentNetwork(user.nick); if (chan) { (0,_router__WEBPACK_IMPORTED_MODULE_2__.switchToChannel)(chan); } _socket__WEBPACK_IMPORTED_MODULE_0__["default"].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]; if (typeof name !== "string") { return ""; } const res = name ? `Revoke ${name} (-${m.mode})` : `Mode -${m.mode}`; return res; }, give(m) { const name = modeCharToName[m.symbol]; if (typeof name !== "string") { return ""; } 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__WEBPACK_IMPORTED_MODULE_0__["default"].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__WEBPACK_IMPORTED_MODULE_0__["default"].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__WEBPACK_IMPORTED_MODULE_0__["default"].emit("input", { target: channel.id, text: "/kick " + user.nick }); } }); } } return items; } /***/ }), /***/ "./client/js/helpers/distance.ts": /*!***************************************!*\ !*** ./client/js/helpers/distance.ts ***! \***************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); function distance([x1, y1], [x2, y2]) { return Math.hypot(x1 - x2, y1 - y2); } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (distance); /***/ }), /***/ "./client/js/helpers/friendlysize.ts": /*!*******************************************!*\ !*** ./client/js/helpers/friendlysize.ts ***! \*******************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); const sizes = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"]; /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (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]}`; }); /***/ }), /***/ "./client/js/helpers/ircmessageparser/anyIntersection.ts": /*!***************************************************************!*\ !*** ./client/js/helpers/ircmessageparser/anyIntersection.ts ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // 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; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (anyIntersection); /***/ }), /***/ "./client/js/helpers/ircmessageparser/cleanIrcMessage.ts": /*!***************************************************************!*\ !*** ./client/js/helpers/ircmessageparser/cleanIrcMessage.ts ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); 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; /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (message => message.replace(matchFormatting, "").trim()); /***/ }), /***/ "./client/js/helpers/ircmessageparser/fill.ts": /*!****************************************************!*\ !*** ./client/js/helpers/ircmessageparser/fill.ts ***! \****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // 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; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (fill); /***/ }), /***/ "./client/js/helpers/ircmessageparser/findChannels.ts": /*!************************************************************!*\ !*** ./client/js/helpers/ircmessageparser/findChannels.ts ***! \************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var lodash_escapeRegExp__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! lodash/escapeRegExp */ "./node_modules/lodash/escapeRegExp.js"); /* harmony import */ var lodash_escapeRegExp__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lodash_escapeRegExp__WEBPACK_IMPORTED_MODULE_0__); // Escapes the RegExp special characters "^", "$", "", ".", "*", "+", "?", "(", // ")", "[", "]", "{", "}", and "|" in string. // See https://lodash.com/docs/#escapeRegExp // escapes a regex in a way that's compatible to shove it in // a regex char set (meaning it also escapes -) function escapeRegExpCharSet(raw) { const escaped = lodash_escapeRegExp__WEBPACK_IMPORTED_MODULE_0___default()(raw); return escaped.replace("-", "\\-"); } // 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(escapeRegExpCharSet).join(""); const channelPrefixPattern = channelPrefixes.map(escapeRegExpCharSet).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({