g(x,c))a[d]=x,a[n]=c,d=n;else break a}}return b}\nfunction g(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}if(\"object\"===typeof performance&&\"function\"===typeof performance.now){var l=performance;exports.unstable_now=function(){return l.now()}}else{var p=Date,q=p.now();exports.unstable_now=function(){return p.now()-q}}var r=[],t=[],u=1,v=null,y=3,z=!1,A=!1,B=!1,D=\"function\"===typeof setTimeout?setTimeout:null,E=\"function\"===typeof clearTimeout?clearTimeout:null,F=\"undefined\"!==typeof setImmediate?setImmediate:null;\n\"undefined\"!==typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function G(a){for(var b=h(t);null!==b;){if(null===b.callback)k(t);else if(b.startTime<=a)k(t),b.sortIndex=b.expirationTime,f(r,b);else break;b=h(t)}}function H(a){B=!1;G(a);if(!A)if(null!==h(r))A=!0,I(J);else{var b=h(t);null!==b&&K(H,b.startTime-a)}}\nfunction J(a,b){A=!1;B&&(B=!1,E(L),L=-1);z=!0;var c=y;try{G(b);for(v=h(r);null!==v&&(!(v.expirationTime>b)||a&&!M());){var d=v.callback;if(\"function\"===typeof d){v.callback=null;y=v.priorityLevel;var e=d(v.expirationTime<=b);b=exports.unstable_now();\"function\"===typeof e?v.callback=e:v===h(r)&&k(r);G(b)}else k(r);v=h(r)}if(null!==v)var w=!0;else{var m=h(t);null!==m&&K(H,m.startTime-b);w=!1}return w}finally{v=null,y=c,z=!1}}var N=!1,O=null,L=-1,P=5,Q=-1;\nfunction M(){return exports.unstable_now()-Qa||125d?(a.sortIndex=c,f(t,a),null===h(r)&&a===h(t)&&(B?(E(L),L=-1):B=!0,K(H,c-d))):(a.sortIndex=e,f(r,a),A||z||(A=!0,I(J)));return a};\nexports.unstable_shouldYield=M;exports.unstable_wrapCallback=function(a){var b=y;return function(){var c=y;y=b;try{return a.apply(this,arguments)}finally{y=c}}};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.min.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.p = \"/\";","export const enum MessageType {\n MESSAGE = \"MESSAGE\",\n CHNAME = \"CHNAME\",\n HELLO = \"HELLO\",\n DATA = \"DATA\",\n}\nexport enum SystemMessageCode {\n REQ,\n RES,\n ERR,\n}\nexport type HistoryFetchResult = {\n count: number;\n items: Array;\n};\nexport type ErrorResult = {\n text: string;\n};\nexport type TimestampSendRequest = {\n ts: number;\n};\nexport type SystemMessage = {\n code: SystemMessageCode;\n data: HistoryFetchResult | ErrorResult | TimestampSendRequest;\n};\nexport type ChatMessage = {\n fromUserId: string;\n toUserId: string;\n content: string;\n timeMillis: number;\n};\nexport type HelloMessage = {\n fromUserId: string;\n timeMillis: number;\n};\nexport type DataMessage = {};\nexport type Message = {\n type: MessageType;\n // data: SystemMessage | ChatMessage | HelloMessage\n fromUserId: string;\n toUserId: string;\n content: string;\n timeMillis: number;\n};\n// Type gymnastics to provide dynamic ESLint support\nexport const acceptedLangs = [\"en_US\", \"zh_TW\", \"el_GR\", \"ar_SA\"] as const;\nexport type LangType = (typeof acceptedLangs)[number];\n","import { createContext } from \"react\";\nimport { LangType } from \"./type/messageTypes\";\nexport type LoginType = {\n\tusername: string;\n\tlastSeen: number;\n\tvalidUntil: number;\n};\nexport const LangContext = createContext(\"en_US\");\nexport const LoginContext = createContext(undefined);\n","import React, { useContext } from \"react\";\nimport { Message, MessageType } from \"../type/messageTypes\";\nimport { LangContext } from \"../context\";\nimport strings from \"../Intl/strings.json\";\nimport \"./MessageDisplay.css\";\nimport { queryByRole } from \"@testing-library/react\";\nexport const MessageDisplay = ({\n\ttype,\n\tfromUserId,\n\ttoUserId,\n\tcontent,\n\ttimeMillis,\n}: Message): React.ReactElement => {\n\tconst dateTime: Date = new Date(timeMillis);\n\tconst lang = useContext(LangContext);\n\tconst msgPage = strings[lang].chat;\n\t/* FIXED funny error\n\t * DESCRIPTION\n\t * The line below was\n\t * return ([{dateTime.toLocaleString(Intl.DateTimeFormat().resolvedOptions().timeZone)}]...
)\n\t * The line incorrectly generated a value of \"UTC\" as the parameter to toLocaleString()\n\t * While \"UTC\" is an accepted string value, in EEST, aka. \"Europe/Athens\" timezone string is not an acceptable parameter.\n\t * This caused the return statement to fail, and the message fails to render, despite it being correctly committed to the db.\n\t * Funny clown moment 🤡\n\t */\n\tconst timeString = `${\n\t\tdateTime.getHours() > 12\n\t\t\t? dateTime.getHours() - 12\n\t\t\t: dateTime.getHours()\n\t}:${\n\t\tdateTime.getMinutes() >= 10\n\t\t\t? dateTime.getMinutes()\n\t\t\t: `0${dateTime.getMinutes().toString()}`\n\t} ${dateTime.getHours() > 12 ? \"PM\" : \"AM\"}`;\n\tswitch (type) {\n\t\tcase MessageType.HELLO as MessageType:\n\t\t\treturn (\n\t\t\t\t\n\t\t\t\t\t[{timeString}]{\" \"}\n\t\t\t\t\t{msgPage.joinMessage.replace(\"$userName\", fromUserId)}\n\t\t\t\t
\n\t\t\t);\n\t\tcase MessageType.MESSAGE as MessageType:\n\t\t\treturn (\n\t\t\t\t\n\t\t\t\t\t[{timeString}]{\" \"}\n\t\t\t\t\t{msgPage.serverMessage\n\t\t\t\t\t\t.replace(\"$userName\", fromUserId)\n\t\t\t\t\t\t.replace(\"$content\", content)}\n\t\t\t\t
\n\t\t\t);\n\t\tcase MessageType.DATA as MessageType:\n\t\t\treturn <>>;\n\t\tcase MessageType.CHNAME as MessageType:\n\t\t\treturn <>>;\n\t\tdefault:\n\t\t\tconsole.error(\"Illegal MessageType reported!\");\n\t\t\treturn (\n\t\t\t\t\n\t\t\t\t\t[{timeString}] **THIS MESSAGE CANNOT BE CORRECTLY SHOWN\n\t\t\t\t\tBECAUSE THE CLIENT ENCOUNTERED AN ERROR**\n\t\t\t\t
\n\t\t\t);\n\t}\n};\n","/**\n * Some byte values, used as per STOMP specifications.\n *\n * Part of `@stomp/stompjs`.\n *\n * @internal\n */\nexport const BYTE = {\n // LINEFEED byte (octet 10)\n LF: '\\x0A',\n // NULL byte (octet 0)\n NULL: '\\x00',\n};\n","import { BYTE } from './byte.js';\nimport { IFrame } from './i-frame.js';\nimport { StompHeaders } from './stomp-headers.js';\nimport { IRawFrameType } from './types.js';\n\n/**\n * Frame class represents a STOMP frame.\n *\n * @internal\n */\nexport class FrameImpl implements IFrame {\n /**\n * STOMP Command\n */\n public command: string;\n\n /**\n * Headers, key value pairs.\n */\n public headers: StompHeaders;\n\n /**\n * Is this frame binary (based on whether body/binaryBody was passed when creating this frame).\n */\n public isBinaryBody: boolean;\n\n /**\n * body of the frame\n */\n get body(): string {\n if (!this._body && this.isBinaryBody) {\n this._body = new TextDecoder().decode(this._binaryBody);\n }\n return this._body || '';\n }\n private _body: string | undefined;\n\n /**\n * body as Uint8Array\n */\n get binaryBody(): Uint8Array {\n if (!this._binaryBody && !this.isBinaryBody) {\n this._binaryBody = new TextEncoder().encode(this._body);\n }\n // At this stage it will definitely have a valid value\n return this._binaryBody as Uint8Array;\n }\n private _binaryBody: Uint8Array | undefined;\n\n private escapeHeaderValues: boolean;\n private skipContentLengthHeader: boolean;\n\n /**\n * Frame constructor. `command`, `headers` and `body` are available as properties.\n *\n * @internal\n */\n constructor(params: {\n command: string;\n headers?: StompHeaders;\n body?: string;\n binaryBody?: Uint8Array;\n escapeHeaderValues?: boolean;\n skipContentLengthHeader?: boolean;\n }) {\n const {\n command,\n headers,\n body,\n binaryBody,\n escapeHeaderValues,\n skipContentLengthHeader,\n } = params;\n this.command = command;\n this.headers = (Object as any).assign({}, headers || {});\n\n if (binaryBody) {\n this._binaryBody = binaryBody;\n this.isBinaryBody = true;\n } else {\n this._body = body || '';\n this.isBinaryBody = false;\n }\n this.escapeHeaderValues = escapeHeaderValues || false;\n this.skipContentLengthHeader = skipContentLengthHeader || false;\n }\n\n /**\n * deserialize a STOMP Frame from raw data.\n *\n * @internal\n */\n public static fromRawFrame(\n rawFrame: IRawFrameType,\n escapeHeaderValues: boolean\n ): FrameImpl {\n const headers: StompHeaders = {};\n const trim = (str: string): string => str.replace(/^\\s+|\\s+$/g, '');\n\n // In case of repeated headers, as per standards, first value need to be used\n for (const header of rawFrame.headers.reverse()) {\n const idx = header.indexOf(':');\n\n const key = trim(header[0]);\n let value = trim(header[1]);\n\n if (\n escapeHeaderValues &&\n rawFrame.command !== 'CONNECT' &&\n rawFrame.command !== 'CONNECTED'\n ) {\n value = FrameImpl.hdrValueUnEscape(value);\n }\n\n headers[key] = value;\n }\n\n return new FrameImpl({\n command: rawFrame.command as string,\n headers,\n binaryBody: rawFrame.binaryBody,\n escapeHeaderValues,\n });\n }\n\n /**\n * @internal\n */\n public toString(): string {\n return this.serializeCmdAndHeaders();\n }\n\n /**\n * serialize this Frame in a format suitable to be passed to WebSocket.\n * If the body is string the output will be string.\n * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer.\n *\n * @internal\n */\n public serialize(): string | ArrayBuffer {\n const cmdAndHeaders = this.serializeCmdAndHeaders();\n\n if (this.isBinaryBody) {\n return FrameImpl.toUnit8Array(\n cmdAndHeaders,\n this._binaryBody as Uint8Array\n ).buffer;\n } else {\n return cmdAndHeaders + this._body + BYTE.NULL;\n }\n }\n\n private serializeCmdAndHeaders(): string {\n const lines = [this.command];\n if (this.skipContentLengthHeader) {\n delete this.headers['content-length'];\n }\n\n for (const name of Object.keys(this.headers || {})) {\n const value = this.headers[name];\n if (\n this.escapeHeaderValues &&\n this.command !== 'CONNECT' &&\n this.command !== 'CONNECTED'\n ) {\n lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`);\n } else {\n lines.push(`${name}:${value}`);\n }\n }\n if (\n this.isBinaryBody ||\n (!this.isBodyEmpty() && !this.skipContentLengthHeader)\n ) {\n lines.push(`content-length:${this.bodyLength()}`);\n }\n return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF;\n }\n\n private isBodyEmpty(): boolean {\n return this.bodyLength() === 0;\n }\n\n private bodyLength(): number {\n const binaryBody = this.binaryBody;\n return binaryBody ? binaryBody.length : 0;\n }\n\n /**\n * Compute the size of a UTF-8 string by counting its number of bytes\n * (and not the number of characters composing the string)\n */\n private static sizeOfUTF8(s: string): number {\n return s ? new TextEncoder().encode(s).length : 0;\n }\n\n private static toUnit8Array(\n cmdAndHeaders: string,\n binaryBody: Uint8Array\n ): Uint8Array {\n const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders);\n const nullTerminator = new Uint8Array([0]);\n const uint8Frame = new Uint8Array(\n uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length\n );\n\n uint8Frame.set(uint8CmdAndHeaders);\n uint8Frame.set(binaryBody, uint8CmdAndHeaders.length);\n uint8Frame.set(\n nullTerminator,\n uint8CmdAndHeaders.length + binaryBody.length\n );\n\n return uint8Frame;\n }\n /**\n * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker.\n *\n * @internal\n */\n public static marshall(params: {\n command: string;\n headers?: StompHeaders;\n body?: string;\n binaryBody?: Uint8Array;\n escapeHeaderValues?: boolean;\n skipContentLengthHeader?: boolean;\n }) {\n const frame = new FrameImpl(params);\n return frame.serialize();\n }\n\n /**\n * Escape header values\n */\n private static hdrValueEscape(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n')\n .replace(/:/g, '\\\\c');\n }\n\n /**\n * UnEscape header values\n */\n private static hdrValueUnEscape(str: string): string {\n return str\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\c/g, ':')\n .replace(/\\\\\\\\/g, '\\\\');\n }\n}\n","import { IRawFrameType } from './types.js';\n\n/**\n * @internal\n */\nconst NULL = 0;\n/**\n * @internal\n */\nconst LF = 10;\n/**\n * @internal\n */\nconst CR = 13;\n/**\n * @internal\n */\nconst COLON = 58;\n\n/**\n * This is an evented, rec descent parser.\n * A stream of Octets can be passed and whenever it recognizes\n * a complete Frame or an incoming ping it will invoke the registered callbacks.\n *\n * All incoming Octets are fed into _onByte function.\n * Depending on current state the _onByte function keeps changing.\n * Depending on the state it keeps accumulating into _token and _results.\n * State is indicated by current value of _onByte, all states are named as _collect.\n *\n * STOMP standards https://stomp.github.io/stomp-specification-1.2.html\n * imply that all lengths are considered in bytes (instead of string lengths).\n * So, before actual parsing, if the incoming data is String it is converted to Octets.\n * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body.\n *\n * There is no peek function on the incoming data.\n * When a state change occurs based on an Octet without consuming the Octet,\n * the Octet, after state change, is fed again (_reinjectByte).\n * This became possible as the state change can be determined by inspecting just one Octet.\n *\n * There are two modes to collect the body, if content-length header is there then it by counting Octets\n * otherwise it is determined by NULL terminator.\n *\n * Following the standards, the command and headers are converted to Strings\n * and the body is returned as Octets.\n * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header.\n *\n * This parser does not use Regular Expressions as that can only operate on Strings.\n *\n * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or\n * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk\n * is pushed.\n *\n * Typically the higher level function will convert headers to Hash, handle unescaping of header values\n * (which is protocol version specific), and convert body to text.\n *\n * Check the parser.spec.js to understand cases that this parser is supposed to handle.\n *\n * Part of `@stomp/stompjs`.\n *\n * @internal\n */\nexport class Parser {\n private readonly _encoder = new TextEncoder();\n private readonly _decoder = new TextDecoder();\n\n // @ts-ignore - it always has a value\n private _results: IRawFrameType;\n\n private _token: number[] = [];\n private _headerKey: string | undefined;\n private _bodyBytesRemaining: number | undefined;\n\n // @ts-ignore - it always has a value\n private _onByte: (byte: number) => void;\n\n public constructor(\n public onFrame: (rawFrame: IRawFrameType) => void,\n public onIncomingPing: () => void\n ) {\n this._initState();\n }\n\n public parseChunk(\n segment: string | ArrayBuffer,\n appendMissingNULLonIncoming: boolean = false\n ) {\n let chunk: Uint8Array;\n\n if (typeof segment === 'string') {\n chunk = this._encoder.encode(segment);\n } else {\n chunk = new Uint8Array(segment);\n }\n\n // See https://github.com/stomp-js/stompjs/issues/89\n // Remove when underlying issue is fixed.\n //\n // Send a NULL byte, if the last byte of a Text frame was not NULL.F\n if (appendMissingNULLonIncoming && chunk[chunk.length - 1] !== 0) {\n const chunkWithNull = new Uint8Array(chunk.length + 1);\n chunkWithNull.set(chunk, 0);\n chunkWithNull[chunk.length] = 0;\n chunk = chunkWithNull;\n }\n\n // tslint:disable-next-line:prefer-for-of\n for (let i = 0; i < chunk.length; i++) {\n const byte = chunk[i];\n this._onByte(byte);\n }\n }\n\n // The following implements a simple Rec Descent Parser.\n // The grammar is simple and just one byte tells what should be the next state\n\n private _collectFrame(byte: number): void {\n if (byte === NULL) {\n // Ignore\n return;\n }\n if (byte === CR) {\n // Ignore CR\n return;\n }\n if (byte === LF) {\n // Incoming Ping\n this.onIncomingPing();\n return;\n }\n\n this._onByte = this._collectCommand;\n this._reinjectByte(byte);\n }\n\n private _collectCommand(byte: number): void {\n if (byte === CR) {\n // Ignore CR\n return;\n }\n if (byte === LF) {\n this._results.command = this._consumeTokenAsUTF8();\n this._onByte = this._collectHeaders;\n return;\n }\n\n this._consumeByte(byte);\n }\n\n private _collectHeaders(byte: number): void {\n if (byte === CR) {\n // Ignore CR\n return;\n }\n if (byte === LF) {\n this._setupCollectBody();\n return;\n }\n this._onByte = this._collectHeaderKey;\n this._reinjectByte(byte);\n }\n\n private _reinjectByte(byte: number) {\n this._onByte(byte);\n }\n\n private _collectHeaderKey(byte: number): void {\n if (byte === COLON) {\n this._headerKey = this._consumeTokenAsUTF8();\n this._onByte = this._collectHeaderValue;\n return;\n }\n this._consumeByte(byte);\n }\n\n private _collectHeaderValue(byte: number): void {\n if (byte === CR) {\n // Ignore CR\n return;\n }\n if (byte === LF) {\n this._results.headers.push([\n this._headerKey as string,\n this._consumeTokenAsUTF8(),\n ]);\n this._headerKey = undefined;\n this._onByte = this._collectHeaders;\n return;\n }\n this._consumeByte(byte);\n }\n\n private _setupCollectBody() {\n const contentLengthHeader = this._results.headers.filter(\n (header: [string, string]) => {\n return header[0] === 'content-length';\n }\n )[0];\n\n if (contentLengthHeader) {\n this._bodyBytesRemaining = parseInt(contentLengthHeader[1], 10);\n this._onByte = this._collectBodyFixedSize;\n } else {\n this._onByte = this._collectBodyNullTerminated;\n }\n }\n\n private _collectBodyNullTerminated(byte: number): void {\n if (byte === NULL) {\n this._retrievedBody();\n return;\n }\n this._consumeByte(byte);\n }\n\n private _collectBodyFixedSize(byte: number): void {\n // It is post decrement, so that we discard the trailing NULL octet\n if ((this._bodyBytesRemaining as number)-- === 0) {\n this._retrievedBody();\n return;\n }\n this._consumeByte(byte);\n }\n\n private _retrievedBody() {\n this._results.binaryBody = this._consumeTokenAsRaw();\n\n try {\n this.onFrame(this._results);\n } catch (e) {\n console.log(\n `Ignoring an exception thrown by a frame handler. Original exception: `,\n e\n );\n }\n\n this._initState();\n }\n\n // Rec Descent Parser helpers\n\n private _consumeByte(byte: number) {\n this._token.push(byte);\n }\n\n private _consumeTokenAsUTF8() {\n return this._decoder.decode(this._consumeTokenAsRaw());\n }\n\n private _consumeTokenAsRaw() {\n const rawResult = new Uint8Array(this._token);\n this._token = [];\n return rawResult;\n }\n\n private _initState() {\n this._results = {\n command: undefined,\n headers: [],\n binaryBody: undefined,\n };\n\n this._token = [];\n this._headerKey = undefined;\n\n this._onByte = this._collectFrame;\n }\n}\n","import { IFrame } from './i-frame.js';\nimport { IMessage } from './i-message.js';\nimport { StompHeaders } from './stomp-headers.js';\nimport { Versions } from './versions.js';\n\n/**\n * This callback will receive a `string` as a parameter.\n *\n * Part of `@stomp/stompjs`.\n */\nexport type debugFnType = (msg: string) => void;\n\n/**\n * This callback will receive a {@link IMessage} as parameter.\n *\n * Part of `@stomp/stompjs`.\n */\nexport type messageCallbackType = (message: IMessage) => void;\n\n/**\n * This callback will receive a {@link IFrame} as parameter.\n *\n * Part of `@stomp/stompjs`.\n */\nexport type frameCallbackType = ((frame: IFrame) => void) | (() => void);\n\n/**\n * This callback will receive a [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}\n * as parameter.\n *\n * Part of `@stomp/stompjs`.\n */\nexport type closeEventCallbackType = (evt: T) => void;\n\n/**\n * This callback will receive an [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event}\n * as parameter.\n *\n * Part of `@stomp/stompjs`.\n */\nexport type wsErrorCallbackType = (evt: T) => void;\n\n/**\n * Parameters for [Client#publish]{@link Client#publish}.\n * Aliased as publishParams as well.\n *\n * Part of `@stomp/stompjs`.\n */\nexport interface IPublishParams {\n /**\n * destination end point\n */\n destination: string;\n /**\n * headers (optional)\n */\n headers?: StompHeaders;\n /**\n * body (optional)\n */\n body?: string;\n /**\n * binary body (optional)\n */\n binaryBody?: Uint8Array;\n /**\n * By default, a `content-length` header will be added in the Frame to the broker.\n * Set it to `true` for the header to be skipped.\n */\n skipContentLengthHeader?: boolean;\n}\n\n/**\n * Backward compatibility, switch to {@link IPublishParams}.\n */\nexport type publishParams = IPublishParams;\n\n/**\n * Used in {@link IRawFrameType}\n *\n * Part of `@stomp/stompjs`.\n *\n * @internal\n */\nexport type RawHeaderType = [string, string];\n\n/**\n * The parser yield frames in this structure\n *\n * Part of `@stomp/stompjs`.\n *\n * @internal\n */\nexport interface IRawFrameType {\n command: string | undefined;\n headers: RawHeaderType[];\n binaryBody: Uint8Array | undefined;\n}\n\n/**\n * @internal\n */\nexport interface IStompSocketMessageEvent {\n data?: string | ArrayBuffer;\n}\n\n/**\n * Copied from Websocket interface to avoid dom typelib dependency.\n *\n * @internal\n */\nexport interface IStompSocket {\n url: string;\n onclose: ((ev?: any) => any) | undefined | null;\n onerror: ((ev: any) => any) | undefined | null;\n onmessage: ((ev: IStompSocketMessageEvent) => any) | undefined | null;\n onopen: ((ev?: any) => any) | undefined | null;\n terminate?: (() => any) | undefined | null;\n\n /**\n * Returns a string that indicates how binary data from the socket is exposed to scripts:\n * We support only 'arraybuffer'.\n */\n binaryType?: string;\n\n /**\n * Returns the state of the socket connection. It can have the values of StompSocketState.\n */\n readonly readyState: number;\n\n /**\n * Closes the connection.\n */\n close(): void;\n /**\n * Transmits data using the connection. data can be a string or an ArrayBuffer.\n */\n send(data: string | ArrayBuffer): void;\n}\n\n/**\n * Possible states for the IStompSocket\n */\nexport enum StompSocketState {\n CONNECTING,\n OPEN,\n CLOSING,\n CLOSED,\n}\n\n/**\n * Possible activation state\n */\nexport enum ActivationState {\n ACTIVE,\n DEACTIVATING,\n INACTIVE,\n}\n\n/**\n * @internal\n */\nexport interface IStomptHandlerConfig {\n debug: debugFnType;\n stompVersions: Versions;\n connectHeaders: StompHeaders;\n disconnectHeaders: StompHeaders;\n heartbeatIncoming: number;\n heartbeatOutgoing: number;\n splitLargeFrames: boolean;\n maxWebSocketChunkSize: number;\n forceBinaryWSFrames: boolean;\n logRawCommunication: boolean;\n appendMissingNULLonIncoming: boolean;\n discardWebsocketOnCommFailure: boolean;\n onConnect: frameCallbackType;\n onDisconnect: frameCallbackType;\n onStompError: frameCallbackType;\n onWebSocketClose: closeEventCallbackType;\n onWebSocketError: wsErrorCallbackType;\n onUnhandledMessage: messageCallbackType;\n onUnhandledReceipt: frameCallbackType;\n onUnhandledFrame: frameCallbackType;\n}\n","/**\n * Supported STOMP versions\n *\n * Part of `@stomp/stompjs`.\n */\nexport class Versions {\n /**\n * Indicates protocol version 1.0\n */\n public static V1_0 = '1.0';\n /**\n * Indicates protocol version 1.1\n */\n public static V1_1 = '1.1';\n /**\n * Indicates protocol version 1.2\n */\n public static V1_2 = '1.2';\n\n /**\n * @internal\n */\n public static default = new Versions([\n Versions.V1_2,\n Versions.V1_1,\n Versions.V1_0,\n ]);\n\n /**\n * Takes an array of versions, typical elements '1.2', '1.1', or '1.0'\n *\n * You will be creating an instance of this class if you want to override\n * supported versions to be declared during STOMP handshake.\n */\n constructor(public versions: string[]) {}\n\n /**\n * Used as part of CONNECT STOMP Frame\n */\n public supportedVersions() {\n return this.versions.join(',');\n }\n\n /**\n * Used while creating a WebSocket\n */\n public protocolVersions() {\n return this.versions.map(x => `v${x.replace('.', '')}.stomp`);\n }\n}\n","import { BYTE } from './byte.js';\nimport { Client } from './client.js';\nimport { FrameImpl } from './frame-impl.js';\nimport { IMessage } from './i-message.js';\nimport { ITransaction } from './i-transaction.js';\nimport { Parser } from './parser.js';\nimport { StompHeaders } from './stomp-headers.js';\nimport { StompSubscription } from './stomp-subscription.js';\nimport {\n closeEventCallbackType,\n debugFnType,\n frameCallbackType,\n IPublishParams,\n IStompSocket,\n IStompSocketMessageEvent,\n IStomptHandlerConfig,\n messageCallbackType,\n StompSocketState,\n wsErrorCallbackType,\n} from './types.js';\nimport { Versions } from './versions.js';\nimport { augmentWebsocket } from './augment-websocket.js';\n\n/**\n * The STOMP protocol handler\n *\n * Part of `@stomp/stompjs`.\n *\n * @internal\n */\nexport class StompHandler {\n public debug: debugFnType;\n\n public stompVersions: Versions;\n\n public connectHeaders: StompHeaders;\n\n public disconnectHeaders: StompHeaders;\n\n public heartbeatIncoming: number;\n\n public heartbeatOutgoing: number;\n\n public onUnhandledMessage: messageCallbackType;\n\n public onUnhandledReceipt: frameCallbackType;\n\n public onUnhandledFrame: frameCallbackType;\n\n public onConnect: frameCallbackType;\n\n public onDisconnect: frameCallbackType;\n\n public onStompError: frameCallbackType;\n\n public onWebSocketClose: closeEventCallbackType;\n\n public onWebSocketError: wsErrorCallbackType;\n\n public logRawCommunication: boolean;\n\n public splitLargeFrames: boolean;\n\n public maxWebSocketChunkSize: number;\n\n public forceBinaryWSFrames: boolean;\n\n public appendMissingNULLonIncoming: boolean;\n\n public discardWebsocketOnCommFailure: boolean;\n\n get connectedVersion(): string | undefined {\n return this._connectedVersion;\n }\n private _connectedVersion: string | undefined;\n\n get connected(): boolean {\n return this._connected;\n }\n\n private _connected: boolean = false;\n\n private readonly _subscriptions: { [key: string]: messageCallbackType };\n private readonly _receiptWatchers: { [key: string]: frameCallbackType };\n private _partialData: string;\n private _escapeHeaderValues: boolean;\n private _counter: number;\n private _pinger: any;\n private _ponger: any;\n private _lastServerActivityTS: number;\n\n constructor(\n private _client: Client,\n public _webSocket: IStompSocket,\n config: IStomptHandlerConfig\n ) {\n // used to index subscribers\n this._counter = 0;\n\n // subscription callbacks indexed by subscriber's ID\n this._subscriptions = {};\n\n // receipt-watchers indexed by receipts-ids\n this._receiptWatchers = {};\n\n this._partialData = '';\n\n this._escapeHeaderValues = false;\n\n this._lastServerActivityTS = Date.now();\n\n this.debug = config.debug;\n this.stompVersions = config.stompVersions;\n this.connectHeaders = config.connectHeaders;\n this.disconnectHeaders = config.disconnectHeaders;\n this.heartbeatIncoming = config.heartbeatIncoming;\n this.heartbeatOutgoing = config.heartbeatOutgoing;\n this.splitLargeFrames = config.splitLargeFrames;\n this.maxWebSocketChunkSize = config.maxWebSocketChunkSize;\n this.forceBinaryWSFrames = config.forceBinaryWSFrames;\n this.logRawCommunication = config.logRawCommunication;\n this.appendMissingNULLonIncoming = config.appendMissingNULLonIncoming;\n this.discardWebsocketOnCommFailure = config.discardWebsocketOnCommFailure;\n this.onConnect = config.onConnect;\n this.onDisconnect = config.onDisconnect;\n this.onStompError = config.onStompError;\n this.onWebSocketClose = config.onWebSocketClose;\n this.onWebSocketError = config.onWebSocketError;\n this.onUnhandledMessage = config.onUnhandledMessage;\n this.onUnhandledReceipt = config.onUnhandledReceipt;\n this.onUnhandledFrame = config.onUnhandledFrame;\n }\n\n public start(): void {\n const parser = new Parser(\n // On Frame\n rawFrame => {\n const frame = FrameImpl.fromRawFrame(\n rawFrame,\n this._escapeHeaderValues\n );\n\n // if this.logRawCommunication is set, the rawChunk is logged at this._webSocket.onmessage\n if (!this.logRawCommunication) {\n this.debug(`<<< ${frame}`);\n }\n\n const serverFrameHandler =\n this._serverFrameHandlers[frame.command] || this.onUnhandledFrame;\n serverFrameHandler(frame);\n },\n // On Incoming Ping\n () => {\n this.debug('<<< PONG');\n }\n );\n\n this._webSocket.onmessage = (evt: IStompSocketMessageEvent) => {\n this.debug('Received data');\n this._lastServerActivityTS = Date.now();\n\n if (this.logRawCommunication) {\n const rawChunkAsString =\n evt.data instanceof ArrayBuffer\n ? new TextDecoder().decode(evt.data)\n : evt.data;\n this.debug(`<<< ${rawChunkAsString}`);\n }\n\n parser.parseChunk(\n evt.data as string | ArrayBuffer,\n this.appendMissingNULLonIncoming\n );\n };\n\n this._webSocket.onclose = (closeEvent): void => {\n this.debug(`Connection closed to ${this._webSocket.url}`);\n this._cleanUp();\n this.onWebSocketClose(closeEvent);\n };\n\n this._webSocket.onerror = (errorEvent): void => {\n this.onWebSocketError(errorEvent);\n };\n\n this._webSocket.onopen = () => {\n // Clone before updating\n const connectHeaders = (Object as any).assign({}, this.connectHeaders);\n\n this.debug('Web Socket Opened...');\n connectHeaders['accept-version'] = this.stompVersions.supportedVersions();\n connectHeaders['heart-beat'] = [\n this.heartbeatOutgoing,\n this.heartbeatIncoming,\n ].join(',');\n this._transmit({ command: 'CONNECT', headers: connectHeaders });\n };\n }\n\n private readonly _serverFrameHandlers: {\n [key: string]: frameCallbackType;\n } = {\n // [CONNECTED Frame](https://stomp.github.com/stomp-specification-1.2.html#CONNECTED_Frame)\n CONNECTED: frame => {\n this.debug(`connected to server ${frame.headers.server}`);\n this._connected = true;\n this._connectedVersion = frame.headers.version;\n // STOMP version 1.2 needs header values to be escaped\n if (this._connectedVersion === Versions.V1_2) {\n this._escapeHeaderValues = true;\n }\n\n this._setupHeartbeat(frame.headers);\n this.onConnect(frame);\n },\n\n // [MESSAGE Frame](https://stomp.github.com/stomp-specification-1.2.html#MESSAGE)\n MESSAGE: frame => {\n // the callback is registered when the client calls\n // `subscribe()`.\n // If there is no registered subscription for the received message,\n // the default `onUnhandledMessage` callback is used that the client can set.\n // This is useful for subscriptions that are automatically created\n // on the browser side (e.g. [RabbitMQ's temporary\n // queues](https://www.rabbitmq.com/stomp.html)).\n const subscription = frame.headers.subscription;\n const onReceive =\n this._subscriptions[subscription] || this.onUnhandledMessage;\n\n // bless the frame to be a Message\n const message = frame as IMessage;\n\n const client = this;\n const messageId =\n this._connectedVersion === Versions.V1_2\n ? message.headers.ack\n : message.headers['message-id'];\n\n // add `ack()` and `nack()` methods directly to the returned frame\n // so that a simple call to `message.ack()` can acknowledge the message.\n message.ack = (headers: StompHeaders = {}): void => {\n return client.ack(messageId, subscription, headers);\n };\n message.nack = (headers: StompHeaders = {}): void => {\n return client.nack(messageId, subscription, headers);\n };\n onReceive(message);\n },\n\n // [RECEIPT Frame](https://stomp.github.com/stomp-specification-1.2.html#RECEIPT)\n RECEIPT: frame => {\n const callback = this._receiptWatchers[frame.headers['receipt-id']];\n if (callback) {\n callback(frame);\n // Server will acknowledge only once, remove the callback\n delete this._receiptWatchers[frame.headers['receipt-id']];\n } else {\n this.onUnhandledReceipt(frame);\n }\n },\n\n // [ERROR Frame](https://stomp.github.com/stomp-specification-1.2.html#ERROR)\n ERROR: frame => {\n this.onStompError(frame);\n },\n };\n\n private _setupHeartbeat(headers: StompHeaders): void {\n if (\n headers.version !== Versions.V1_1 &&\n headers.version !== Versions.V1_2\n ) {\n return;\n }\n\n // It is valid for the server to not send this header\n // https://stomp.github.io/stomp-specification-1.2.html#Heart-beating\n if (!headers['heart-beat']) {\n return;\n }\n\n // heart-beat header received from the server looks like:\n //\n // heart-beat: sx, sy\n const [serverOutgoing, serverIncoming] = headers['heart-beat']\n .split(',')\n .map((v: string) => parseInt(v, 10));\n\n if (this.heartbeatOutgoing !== 0 && serverIncoming !== 0) {\n const ttl: number = Math.max(this.heartbeatOutgoing, serverIncoming);\n this.debug(`send PING every ${ttl}ms`);\n this._pinger = setInterval(() => {\n if (this._webSocket.readyState === StompSocketState.OPEN) {\n this._webSocket.send(BYTE.LF);\n this.debug('>>> PING');\n }\n }, ttl);\n }\n\n if (this.heartbeatIncoming !== 0 && serverOutgoing !== 0) {\n const ttl: number = Math.max(this.heartbeatIncoming, serverOutgoing);\n this.debug(`check PONG every ${ttl}ms`);\n this._ponger = setInterval(() => {\n const delta = Date.now() - this._lastServerActivityTS;\n // We wait twice the TTL to be flexible on window's setInterval calls\n if (delta > ttl * 2) {\n this.debug(`did not receive server activity for the last ${delta}ms`);\n this._closeOrDiscardWebsocket();\n }\n }, ttl);\n }\n }\n\n private _closeOrDiscardWebsocket() {\n if (this.discardWebsocketOnCommFailure) {\n this.debug(\n 'Discarding websocket, the underlying socket may linger for a while'\n );\n this.discardWebsocket();\n } else {\n this.debug('Issuing close on the websocket');\n this._closeWebsocket();\n }\n }\n\n public forceDisconnect() {\n if (this._webSocket) {\n if (\n this._webSocket.readyState === StompSocketState.CONNECTING ||\n this._webSocket.readyState === StompSocketState.OPEN\n ) {\n this._closeOrDiscardWebsocket();\n }\n }\n }\n\n public _closeWebsocket() {\n this._webSocket.onmessage = () => {}; // ignore messages\n this._webSocket.close();\n }\n\n public discardWebsocket() {\n if (typeof this._webSocket.terminate !== 'function') {\n augmentWebsocket(this._webSocket, (msg: string) => this.debug(msg));\n }\n\n // @ts-ignore - this method will be there at this stage\n this._webSocket.terminate();\n }\n\n private _transmit(params: {\n command: string;\n headers?: StompHeaders;\n body?: string;\n binaryBody?: Uint8Array;\n skipContentLengthHeader?: boolean;\n }): void {\n const { command, headers, body, binaryBody, skipContentLengthHeader } =\n params;\n const frame = new FrameImpl({\n command,\n headers,\n body,\n binaryBody,\n escapeHeaderValues: this._escapeHeaderValues,\n skipContentLengthHeader,\n });\n\n let rawChunk = frame.serialize();\n\n if (this.logRawCommunication) {\n this.debug(`>>> ${rawChunk}`);\n } else {\n this.debug(`>>> ${frame}`);\n }\n\n if (this.forceBinaryWSFrames && typeof rawChunk === 'string') {\n rawChunk = new TextEncoder().encode(rawChunk);\n }\n\n if (typeof rawChunk !== 'string' || !this.splitLargeFrames) {\n this._webSocket.send(rawChunk);\n } else {\n let out = rawChunk as string;\n while (out.length > 0) {\n const chunk = out.substring(0, this.maxWebSocketChunkSize);\n out = out.substring(this.maxWebSocketChunkSize);\n this._webSocket.send(chunk);\n this.debug(`chunk sent = ${chunk.length}, remaining = ${out.length}`);\n }\n }\n }\n\n public dispose(): void {\n if (this.connected) {\n try {\n // clone before updating\n const disconnectHeaders = (Object as any).assign(\n {},\n this.disconnectHeaders\n );\n\n if (!disconnectHeaders.receipt) {\n disconnectHeaders.receipt = `close-${this._counter++}`;\n }\n this.watchForReceipt(disconnectHeaders.receipt, frame => {\n this._closeWebsocket();\n this._cleanUp();\n this.onDisconnect(frame);\n });\n this._transmit({ command: 'DISCONNECT', headers: disconnectHeaders });\n } catch (error) {\n this.debug(`Ignoring error during disconnect ${error}`);\n }\n } else {\n if (\n this._webSocket.readyState === StompSocketState.CONNECTING ||\n this._webSocket.readyState === StompSocketState.OPEN\n ) {\n this._closeWebsocket();\n }\n }\n }\n\n private _cleanUp() {\n this._connected = false;\n\n if (this._pinger) {\n clearInterval(this._pinger);\n this._pinger = undefined;\n }\n if (this._ponger) {\n clearInterval(this._ponger);\n this._ponger = undefined;\n }\n }\n\n public publish(params: IPublishParams): void {\n const { destination, headers, body, binaryBody, skipContentLengthHeader } =\n params;\n const hdrs: StompHeaders = (Object as any).assign({ destination }, headers);\n this._transmit({\n command: 'SEND',\n headers: hdrs,\n body,\n binaryBody,\n skipContentLengthHeader,\n });\n }\n\n public watchForReceipt(receiptId: string, callback: frameCallbackType): void {\n this._receiptWatchers[receiptId] = callback;\n }\n\n public subscribe(\n destination: string,\n callback: messageCallbackType,\n headers: StompHeaders = {}\n ): StompSubscription {\n headers = (Object as any).assign({}, headers);\n\n if (!headers.id) {\n headers.id = `sub-${this._counter++}`;\n }\n headers.destination = destination;\n this._subscriptions[headers.id] = callback;\n this._transmit({ command: 'SUBSCRIBE', headers });\n const client = this;\n return {\n id: headers.id,\n\n unsubscribe(hdrs) {\n return client.unsubscribe(headers.id, hdrs);\n },\n };\n }\n\n public unsubscribe(id: string, headers: StompHeaders = {}): void {\n headers = (Object as any).assign({}, headers);\n\n delete this._subscriptions[id];\n headers.id = id;\n this._transmit({ command: 'UNSUBSCRIBE', headers });\n }\n\n public begin(transactionId: string): ITransaction {\n const txId = transactionId || `tx-${this._counter++}`;\n this._transmit({\n command: 'BEGIN',\n headers: {\n transaction: txId,\n },\n });\n const client = this;\n return {\n id: txId,\n commit(): void {\n client.commit(txId);\n },\n abort(): void {\n client.abort(txId);\n },\n };\n }\n\n public commit(transactionId: string): void {\n this._transmit({\n command: 'COMMIT',\n headers: {\n transaction: transactionId,\n },\n });\n }\n\n public abort(transactionId: string): void {\n this._transmit({\n command: 'ABORT',\n headers: {\n transaction: transactionId,\n },\n });\n }\n\n public ack(\n messageId: string,\n subscriptionId: string,\n headers: StompHeaders = {}\n ): void {\n headers = (Object as any).assign({}, headers);\n\n if (this._connectedVersion === Versions.V1_2) {\n headers.id = messageId;\n } else {\n headers['message-id'] = messageId;\n }\n headers.subscription = subscriptionId;\n this._transmit({ command: 'ACK', headers });\n }\n\n public nack(\n messageId: string,\n subscriptionId: string,\n headers: StompHeaders = {}\n ): void {\n headers = (Object as any).assign({}, headers);\n\n if (this._connectedVersion === Versions.V1_2) {\n headers.id = messageId;\n } else {\n headers['message-id'] = messageId;\n }\n headers.subscription = subscriptionId;\n return this._transmit({ command: 'NACK', headers });\n }\n}\n","import { IStompSocket } from './types.js';\n\n/**\n * @internal\n */\nexport function augmentWebsocket(\n webSocket: IStompSocket,\n debug: (msg: string) => void\n) {\n webSocket.terminate = function () {\n const noOp = () => {};\n\n // set all callbacks to no op\n this.onerror = noOp;\n this.onmessage = noOp;\n this.onopen = noOp;\n\n const ts = new Date();\n const id = Math.random().toString().substring(2, 8); // A simulated id\n\n const origOnClose = this.onclose;\n\n // Track delay in actual closure of the socket\n this.onclose = closeEvent => {\n const delay = new Date().getTime() - ts.getTime();\n debug(\n `Discarded socket (#${id}) closed after ${delay}ms, with code/reason: ${closeEvent.code}/${closeEvent.reason}`\n );\n };\n\n this.close();\n\n origOnClose?.call(webSocket, {\n code: 4001,\n reason: `Quick discarding socket (#${id}) without waiting for the shutdown sequence.`,\n wasClean: false,\n });\n };\n}\n","import { ITransaction } from './i-transaction.js';\nimport { StompConfig } from './stomp-config.js';\nimport { StompHandler } from './stomp-handler.js';\nimport { StompHeaders } from './stomp-headers.js';\nimport { StompSubscription } from './stomp-subscription.js';\nimport {\n ActivationState,\n closeEventCallbackType,\n debugFnType,\n frameCallbackType,\n IPublishParams,\n IStompSocket,\n messageCallbackType,\n StompSocketState,\n wsErrorCallbackType,\n} from './types.js';\nimport { Versions } from './versions.js';\n\n/**\n * @internal\n */\ndeclare const WebSocket: {\n prototype: IStompSocket;\n new (url: string, protocols?: string | string[]): IStompSocket;\n};\n\n/**\n * STOMP Client Class.\n *\n * Part of `@stomp/stompjs`.\n */\nexport class Client {\n /**\n * The URL for the STOMP broker to connect to.\n * Typically like `\"ws://broker.329broker.com:15674/ws\"` or `\"wss://broker.329broker.com:15674/ws\"`.\n *\n * Only one of this or [Client#webSocketFactory]{@link Client#webSocketFactory} need to be set.\n * If both are set, [Client#webSocketFactory]{@link Client#webSocketFactory} will be used.\n *\n * If your environment does not support WebSockets natively, please refer to\n * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.\n */\n public brokerURL: string | undefined;\n\n /**\n * STOMP versions to attempt during STOMP handshake. By default, versions `1.2`, `1.1`, and `1.0` are attempted.\n *\n * Example:\n * ```javascript\n * // Try only versions 1.1 and 1.0\n * client.stompVersions = new Versions(['1.1', '1.0'])\n * ```\n */\n public stompVersions = Versions.default;\n\n /**\n * This function should return a WebSocket or a similar (e.g. SockJS) object.\n * If your environment does not support WebSockets natively, please refer to\n * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.\n * If your STOMP Broker supports WebSockets, prefer setting [Client#brokerURL]{@link Client#brokerURL}.\n *\n * If both this and [Client#brokerURL]{@link Client#brokerURL} are set, this will be used.\n *\n * Example:\n * ```javascript\n * // use a WebSocket\n * client.webSocketFactory= function () {\n * return new WebSocket(\"wss://broker.329broker.com:15674/ws\");\n * };\n *\n * // Typical usage with SockJS\n * client.webSocketFactory= function () {\n * return new SockJS(\"http://broker.329broker.com/stomp\");\n * };\n * ```\n */\n public webSocketFactory: (() => IStompSocket) | undefined;\n\n /**\n * Will retry if Stomp connection is not established in specified milliseconds.\n * Default 0, which switches off automatic reconnection.\n */\n public connectionTimeout: number = 0;\n\n // As per https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window/56239226#56239226\n private _connectionWatcher: ReturnType | undefined; // Timer\n\n /**\n * automatically reconnect with delay in milliseconds, set to 0 to disable.\n */\n public reconnectDelay: number = 5000;\n\n /**\n * Incoming heartbeat interval in milliseconds. Set to 0 to disable.\n */\n public heartbeatIncoming: number = 10000;\n\n /**\n * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.\n */\n public heartbeatOutgoing: number = 10000;\n\n /**\n * This switches on a non-standard behavior while sending WebSocket packets.\n * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.\n * Only Java Spring brokers seem to support this mode.\n *\n * WebSockets, by itself, split large (text) packets,\n * so it is not needed with a truly compliant STOMP/WebSocket broker.\n * Setting it for such a broker will cause large messages to fail.\n *\n * `false` by default.\n *\n * Binary frames are never split.\n */\n public splitLargeFrames: boolean = false;\n\n /**\n * See [splitLargeFrames]{@link Client#splitLargeFrames}.\n * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.\n */\n public maxWebSocketChunkSize: number = 8 * 1024;\n\n /**\n * Usually the\n * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}\n * is automatically decided by type of the payload.\n * Default is `false`, which should work with all compliant brokers.\n *\n * Set this flag to force binary frames.\n */\n public forceBinaryWSFrames: boolean = false;\n\n /**\n * A bug in ReactNative chops a string on occurrence of a NULL.\n * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.\n * This makes incoming WebSocket messages invalid STOMP packets.\n * Setting this flag attempts to reverse the damage by appending a NULL.\n * If the broker splits a large message into multiple WebSocket messages,\n * this flag will cause data loss and abnormal termination of connection.\n *\n * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.\n */\n public appendMissingNULLonIncoming: boolean = false;\n\n /**\n * Underlying WebSocket instance, READONLY.\n */\n get webSocket(): IStompSocket | undefined {\n return this._stompHandler?._webSocket;\n }\n\n /**\n * Connection headers, important keys - `login`, `passcode`, `host`.\n * Though STOMP 1.2 standard marks these keys to be present, check your broker documentation for\n * details specific to your broker.\n */\n public connectHeaders: StompHeaders;\n\n /**\n * Disconnection headers.\n */\n get disconnectHeaders(): StompHeaders {\n return this._disconnectHeaders;\n }\n\n set disconnectHeaders(value: StompHeaders) {\n this._disconnectHeaders = value;\n if (this._stompHandler) {\n this._stompHandler.disconnectHeaders = this._disconnectHeaders;\n }\n }\n private _disconnectHeaders: StompHeaders;\n\n /**\n * This function will be called for any unhandled messages.\n * It is useful for receiving messages sent to RabbitMQ temporary queues.\n *\n * It can also get invoked with stray messages while the server is processing\n * a request to [Client#unsubscribe]{@link Client#unsubscribe}\n * from an endpoint.\n *\n * The actual {@link IMessage} will be passed as parameter to the callback.\n */\n public onUnhandledMessage: messageCallbackType;\n\n /**\n * STOMP brokers can be requested to notify when an operation is actually completed.\n * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}. See\n * [Client#watchForReceipt]{@link Client#watchForReceipt} for examples.\n *\n * The actual {@link IFrame} will be passed as parameter to the callback.\n */\n public onUnhandledReceipt: frameCallbackType;\n\n /**\n * Will be invoked if {@link IFrame} of an unknown type is received from the STOMP broker.\n *\n * The actual {@link IFrame} will be passed as parameter to the callback.\n */\n public onUnhandledFrame: frameCallbackType;\n\n /**\n * `true` if there is an active connection to STOMP Broker\n */\n get connected(): boolean {\n return !!this._stompHandler && this._stompHandler.connected;\n }\n\n /**\n * Callback, invoked on before a connection to the STOMP broker.\n *\n * You can change options on the client, which will impact the immediate connecting.\n * It is valid to call [Client#decativate]{@link Client#deactivate} in this callback.\n *\n * As of version 5.1, this callback can be\n * [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * (i.e., it can return a\n * [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)).\n * In that case, connect will be called only after the Promise is resolved.\n * This can be used to reliably fetch credentials, access token etc. from some other service\n * in an asynchronous way.\n */\n public beforeConnect: () => void | Promise;\n\n /**\n * Callback, invoked on every successful connection to the STOMP broker.\n *\n * The actual {@link IFrame} will be passed as parameter to the callback.\n * Sometimes clients will like to use headers from this frame.\n */\n public onConnect: frameCallbackType;\n\n /**\n * Callback, invoked on every successful disconnection from the STOMP broker. It will not be invoked if\n * the STOMP broker disconnected due to an error.\n *\n * The actual Receipt {@link IFrame} acknowledging the DISCONNECT will be passed as parameter to the callback.\n *\n * The way STOMP protocol is designed, the connection may close/terminate without the client\n * receiving the Receipt {@link IFrame} acknowledging the DISCONNECT.\n * You might find [Client#onWebSocketClose]{@link Client#onWebSocketClose} more appropriate to watch\n * STOMP broker disconnects.\n */\n public onDisconnect: frameCallbackType;\n\n /**\n * Callback, invoked on an ERROR frame received from the STOMP Broker.\n * A compliant STOMP Broker will close the connection after this type of frame.\n * Please check broker specific documentation for exact behavior.\n *\n * The actual {@link IFrame} will be passed as parameter to the callback.\n */\n public onStompError: frameCallbackType;\n\n /**\n * Callback, invoked when underlying WebSocket is closed.\n *\n * Actual [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}\n * is passed as parameter to the callback.\n */\n public onWebSocketClose: closeEventCallbackType;\n\n /**\n * Callback, invoked when underlying WebSocket raises an error.\n *\n * Actual [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event}\n * is passed as parameter to the callback.\n */\n public onWebSocketError: wsErrorCallbackType;\n\n /**\n * Set it to log the actual raw communication with the broker.\n * When unset, it logs headers of the parsed frames.\n *\n * Changes effect from the next broker reconnect.\n *\n * **Caution: this assumes that frames only have valid UTF8 strings.**\n */\n public logRawCommunication: boolean;\n\n /**\n * By default, debug messages are discarded. To log to `console` following can be used:\n *\n * ```javascript\n * client.debug = function(str) {\n * console.log(str);\n * };\n * ```\n *\n * Currently this method does not support levels of log. Be aware that the\n * output can be quite verbose\n * and may contain sensitive information (like passwords, tokens etc.).\n */\n public debug: debugFnType;\n\n /**\n * Browsers do not immediately close WebSockets when `.close` is issued.\n * This may cause reconnection to take a significantly long time in case\n * of some types of failures.\n * In case of incoming heartbeat failure, this experimental flag instructs\n * the library to discard the socket immediately\n * (even before it is actually closed).\n */\n public discardWebsocketOnCommFailure: boolean = false;\n\n /**\n * version of STOMP protocol negotiated with the server, READONLY\n */\n get connectedVersion(): string | undefined {\n return this._stompHandler ? this._stompHandler.connectedVersion : undefined;\n }\n\n private _stompHandler: StompHandler | undefined;\n\n /**\n * if the client is active (connected or going to reconnect)\n */\n get active(): boolean {\n return this.state === ActivationState.ACTIVE;\n }\n\n /**\n * It will be called on state change.\n *\n * When deactivating, it may go from ACTIVE to INACTIVE without entering DEACTIVATING.\n */\n public onChangeState: (state: ActivationState) => void;\n\n private _changeState(state: ActivationState) {\n this.state = state;\n this.onChangeState(state);\n }\n\n /**\n * Activation state.\n *\n * It will usually be ACTIVE or INACTIVE.\n * When deactivating, it may go from ACTIVE to INACTIVE without entering DEACTIVATING.\n */\n public state: ActivationState = ActivationState.INACTIVE;\n\n private _reconnector: any;\n\n /**\n * Create an instance.\n */\n constructor(conf: StompConfig = {}) {\n // No op callbacks\n const noOp = () => {};\n this.debug = noOp;\n this.beforeConnect = noOp;\n this.onConnect = noOp;\n this.onDisconnect = noOp;\n this.onUnhandledMessage = noOp;\n this.onUnhandledReceipt = noOp;\n this.onUnhandledFrame = noOp;\n this.onStompError = noOp;\n this.onWebSocketClose = noOp;\n this.onWebSocketError = noOp;\n this.logRawCommunication = false;\n this.onChangeState = noOp;\n\n // These parameters would typically get proper values before connect is called\n this.connectHeaders = {};\n this._disconnectHeaders = {};\n\n // Apply configuration\n this.configure(conf);\n }\n\n /**\n * Update configuration.\n */\n public configure(conf: StompConfig): void {\n // bulk assign all properties to this\n (Object as any).assign(this, conf);\n }\n\n /**\n * Initiate the connection with the broker.\n * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},\n * it will keep trying to reconnect.\n *\n * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.\n */\n public activate(): void {\n const _activate = () => {\n if (this.active) {\n this.debug('Already ACTIVE, ignoring request to activate');\n return;\n }\n\n this._changeState(ActivationState.ACTIVE);\n\n this._connect();\n };\n\n // if it is deactivating, wait for it to complete before activating.\n if (this.state === ActivationState.DEACTIVATING) {\n this.debug('Waiting for deactivation to finish before activating');\n this.deactivate().then(() => {\n _activate();\n });\n } else {\n _activate();\n }\n }\n\n private async _connect(): Promise {\n await this.beforeConnect();\n\n if (this._stompHandler) {\n this.debug('There is already a stompHandler, skipping the call to connect');\n return;\n }\n\n if (!this.active) {\n this.debug(\n 'Client has been marked inactive, will not attempt to connect'\n );\n return;\n }\n\n // setup connection watcher\n if (this.connectionTimeout > 0) {\n // clear first\n if (this._connectionWatcher) {\n clearTimeout(this._connectionWatcher);\n }\n this._connectionWatcher = setTimeout(() => {\n if (this.connected) {\n return;\n }\n // Connection not established, close the underlying socket\n // a reconnection will be attempted\n this.debug(\n `Connection not established in ${this.connectionTimeout}ms, closing socket`\n );\n this.forceDisconnect();\n }, this.connectionTimeout);\n }\n\n this.debug('Opening Web Socket...');\n\n // Get the actual WebSocket (or a similar object)\n const webSocket = this._createWebSocket();\n\n this._stompHandler = new StompHandler(this, webSocket, {\n debug: this.debug,\n stompVersions: this.stompVersions,\n connectHeaders: this.connectHeaders,\n disconnectHeaders: this._disconnectHeaders,\n heartbeatIncoming: this.heartbeatIncoming,\n heartbeatOutgoing: this.heartbeatOutgoing,\n splitLargeFrames: this.splitLargeFrames,\n maxWebSocketChunkSize: this.maxWebSocketChunkSize,\n forceBinaryWSFrames: this.forceBinaryWSFrames,\n logRawCommunication: this.logRawCommunication,\n appendMissingNULLonIncoming: this.appendMissingNULLonIncoming,\n discardWebsocketOnCommFailure: this.discardWebsocketOnCommFailure,\n\n onConnect: frame => {\n // Successfully connected, stop the connection watcher\n if (this._connectionWatcher) {\n clearTimeout(this._connectionWatcher);\n this._connectionWatcher = undefined;\n }\n\n if (!this.active) {\n this.debug(\n 'STOMP got connected while deactivate was issued, will disconnect now'\n );\n this._disposeStompHandler();\n return;\n }\n this.onConnect(frame);\n },\n onDisconnect: frame => {\n this.onDisconnect(frame);\n },\n onStompError: frame => {\n this.onStompError(frame);\n },\n onWebSocketClose: evt => {\n this._stompHandler = undefined; // a new one will be created in case of a reconnect\n\n if (this.state === ActivationState.DEACTIVATING) {\n // Mark deactivation complete\n this._changeState(ActivationState.INACTIVE);\n }\n\n // The callback is called before attempting to reconnect, this would allow the client\n // to be `deactivated` in the callback.\n this.onWebSocketClose(evt);\n\n if (this.active) {\n this._schedule_reconnect();\n }\n },\n onWebSocketError: evt => {\n this.onWebSocketError(evt);\n },\n onUnhandledMessage: message => {\n this.onUnhandledMessage(message);\n },\n onUnhandledReceipt: frame => {\n this.onUnhandledReceipt(frame);\n },\n onUnhandledFrame: frame => {\n this.onUnhandledFrame(frame);\n },\n });\n\n this._stompHandler.start();\n }\n\n private _createWebSocket(): IStompSocket {\n let webSocket: IStompSocket;\n\n if (this.webSocketFactory) {\n webSocket = this.webSocketFactory();\n } else if (this.brokerURL) {\n webSocket = new WebSocket(\n this.brokerURL,\n this.stompVersions.protocolVersions()\n );\n } else {\n throw new Error('Either brokerURL or webSocketFactory must be provided');\n }\n webSocket.binaryType = 'arraybuffer';\n return webSocket;\n }\n\n private _schedule_reconnect(): void {\n if (this.reconnectDelay > 0) {\n this.debug(`STOMP: scheduling reconnection in ${this.reconnectDelay}ms`);\n\n this._reconnector = setTimeout(() => {\n this._connect();\n }, this.reconnectDelay);\n }\n }\n\n /**\n * Disconnect if connected and stop auto reconnect loop.\n * Appropriate callbacks will be invoked if there is an underlying STOMP connection.\n *\n * This call is async. It will resolve immediately if there is no underlying active websocket,\n * otherwise, it will resolve after the underlying websocket is properly disposed of.\n *\n * It is not an error to invoke this method more than once.\n * Each of those would resolve on completion of deactivation.\n *\n * To reactivate, you can call [Client#activate]{@link Client#activate}.\n *\n * Experimental: pass `force: true` to immediately discard the underlying connection.\n * This mode will skip both the STOMP and the Websocket shutdown sequences.\n * In some cases, browsers take a long time in the Websocket shutdown\n * if the underlying connection had gone stale.\n * Using this mode can speed up.\n * When this mode is used, the actual Websocket may linger for a while\n * and the broker may not realize that the connection is no longer in use.\n *\n * It is possible to invoke this method initially without the `force` option\n * and subsequently, say after a wait, with the `force` option.\n */\n public async deactivate(options: { force?: boolean } = {}): Promise {\n const force: boolean = options.force || false;\n const needToDispose = this.active;\n let retPromise: Promise;\n\n if (this.state === ActivationState.INACTIVE) {\n this.debug(`Already INACTIVE, nothing more to do`);\n return Promise.resolve();\n }\n\n this._changeState(ActivationState.DEACTIVATING);\n\n // Clear if a reconnection was scheduled\n if (this._reconnector) {\n clearTimeout(this._reconnector);\n this._reconnector = undefined;\n }\n\n if (\n this._stompHandler &&\n // @ts-ignore - if there is a _stompHandler, there is the webSocket\n this.webSocket.readyState !== StompSocketState.CLOSED\n ) {\n const origOnWebSocketClose = this._stompHandler.onWebSocketClose;\n // we need to wait for the underlying websocket to close\n retPromise = new Promise((resolve, reject) => {\n // @ts-ignore - there is a _stompHandler\n this._stompHandler.onWebSocketClose = evt => {\n origOnWebSocketClose(evt);\n resolve();\n };\n });\n } else {\n // indicate that auto reconnect loop should terminate\n this._changeState(ActivationState.INACTIVE);\n return Promise.resolve();\n }\n\n if (force) {\n this._stompHandler?.discardWebsocket();\n } else if (needToDispose) {\n this._disposeStompHandler();\n }\n\n return retPromise;\n }\n\n /**\n * Force disconnect if there is an active connection by directly closing the underlying WebSocket.\n * This is different from a normal disconnect where a DISCONNECT sequence is carried out with the broker.\n * After forcing disconnect, automatic reconnect will be attempted.\n * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.\n */\n public forceDisconnect() {\n if (this._stompHandler) {\n this._stompHandler.forceDisconnect();\n }\n }\n\n private _disposeStompHandler() {\n // Dispose STOMP Handler\n if (this._stompHandler) {\n this._stompHandler.dispose();\n }\n }\n\n /**\n * Send a message to a named destination. Refer to your STOMP broker documentation for types\n * and naming of destinations.\n *\n * STOMP protocol specifies and suggests some headers and also allows broker-specific headers.\n *\n * `body` must be String.\n * You will need to covert the payload to string in case it is not string (e.g. JSON).\n *\n * To send a binary message body, use `binaryBody` parameter. It should be a\n * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).\n * Sometimes brokers may not support binary frames out of the box.\n * Please check your broker documentation.\n *\n * `content-length` header is automatically added to the STOMP Frame sent to the broker.\n * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.\n * For binary messages, `content-length` header is always added.\n *\n * Caution: The broker will, most likely, report an error and disconnect\n * if the message body has NULL octet(s) and `content-length` header is missing.\n *\n * ```javascript\n * client.publish({destination: \"/queue/test\", headers: {priority: 9}, body: \"Hello, STOMP\"});\n *\n * // Only destination is mandatory parameter\n * client.publish({destination: \"/queue/test\", body: \"Hello, STOMP\"});\n *\n * // Skip content-length header in the frame to the broker\n * client.publish({\"/queue/test\", body: \"Hello, STOMP\", skipContentLengthHeader: true});\n *\n * var binaryData = generateBinaryData(); // This need to be of type Uint8Array\n * // setting content-type header is not mandatory, however a good practice\n * client.publish({destination: '/topic/special', binaryBody: binaryData,\n * headers: {'content-type': 'application/octet-stream'}});\n * ```\n */\n public publish(params: IPublishParams) {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.publish(params);\n }\n\n private _checkConnection() {\n if (!this.connected) {\n throw new TypeError('There is no underlying STOMP connection');\n }\n }\n\n /**\n * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.\n * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.\n * The value (say receipt-id) for this header needs to be unique for each use.\n * Typically, a sequence, a UUID, a random number or a combination may be used.\n *\n * A complaint broker will send a RECEIPT frame when an operation has actually been completed.\n * The operation needs to be matched based on the value of the receipt-id.\n *\n * This method allows watching for a receipt and invoking the callback\n * when the corresponding receipt has been received.\n *\n * The actual {@link IFrame} will be passed as parameter to the callback.\n *\n * Example:\n * ```javascript\n * // Subscribing with acknowledgement\n * let receiptId = randomText();\n *\n * client.watchForReceipt(receiptId, function() {\n * // Will be called after server acknowledges\n * });\n *\n * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});\n *\n *\n * // Publishing with acknowledgement\n * receiptId = randomText();\n *\n * client.watchForReceipt(receiptId, function() {\n * // Will be called after server acknowledges\n * });\n * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});\n * ```\n */\n public watchForReceipt(receiptId: string, callback: frameCallbackType): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.watchForReceipt(receiptId, callback);\n }\n\n /**\n * Subscribe to a STOMP Broker location. The callback will be invoked for each\n * received message with the {@link IMessage} as argument.\n *\n * Note: The library will generate a unique ID if there is none provided in the headers.\n * To use your own ID, pass it using the `headers` argument.\n *\n * ```javascript\n * callback = function(message) {\n * // called when the client receives a STOMP message from the server\n * if (message.body) {\n * alert(\"got message with body \" + message.body)\n * } else {\n * alert(\"got empty message\");\n * }\n * });\n *\n * var subscription = client.subscribe(\"/queue/test\", callback);\n *\n * // Explicit subscription id\n * var mySubId = 'my-subscription-id-001';\n * var subscription = client.subscribe(destination, callback, { id: mySubId });\n * ```\n */\n public subscribe(\n destination: string,\n callback: messageCallbackType,\n headers: StompHeaders = {}\n ): StompSubscription {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n return this._stompHandler.subscribe(destination, callback, headers);\n }\n\n /**\n * It is preferable to unsubscribe from a subscription by calling\n * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:\n *\n * ```javascript\n * var subscription = client.subscribe(destination, onmessage);\n * // ...\n * subscription.unsubscribe();\n * ```\n *\n * See: https://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame\n */\n public unsubscribe(id: string, headers: StompHeaders = {}): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.unsubscribe(id, headers);\n }\n\n /**\n * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}\n * and [abort]{@link ITransaction#abort}.\n *\n * `transactionId` is optional, if not passed the library will generate it internally.\n */\n public begin(transactionId?: string): ITransaction {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n return this._stompHandler.begin(transactionId);\n }\n\n /**\n * Commit a transaction.\n *\n * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on\n * {@link ITransaction} returned by [client.begin]{@link Client#begin}.\n *\n * ```javascript\n * var tx = client.begin(txId);\n * //...\n * tx.commit();\n * ```\n */\n public commit(transactionId: string): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.commit(transactionId);\n }\n\n /**\n * Abort a transaction.\n * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on\n * {@link ITransaction} returned by [client.begin]{@link Client#begin}.\n *\n * ```javascript\n * var tx = client.begin(txId);\n * //...\n * tx.abort();\n * ```\n */\n public abort(transactionId: string): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.abort(transactionId);\n }\n\n /**\n * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly\n * on the {@link IMessage} handled by a subscription callback:\n *\n * ```javascript\n * var callback = function (message) {\n * // process the message\n * // acknowledge it\n * message.ack();\n * };\n * client.subscribe(destination, callback, {'ack': 'client'});\n * ```\n */\n public ack(\n messageId: string,\n subscriptionId: string,\n headers: StompHeaders = {}\n ): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.ack(messageId, subscriptionId, headers);\n }\n\n /**\n * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly\n * on the {@link IMessage} handled by a subscription callback:\n *\n * ```javascript\n * var callback = function (message) {\n * // process the message\n * // an error occurs, nack it\n * message.nack();\n * };\n * client.subscribe(destination, callback, {'ack': 'client'});\n * ```\n */\n public nack(\n messageId: string,\n subscriptionId: string,\n headers: StompHeaders = {}\n ): void {\n this._checkConnection();\n // @ts-ignore - we already checked that there is a _stompHandler, and it is connected\n this._stompHandler.nack(messageId, subscriptionId, headers);\n }\n}\n","export const domain = window.location.hostname;\nexport const port = \"8080\";\nexport const connectionAddress = `ws://${domain}:${port}/ws`;\nexport const endpoints = {\n\tdestination: \"/app/chat\",\n\tsubscription: \"/sub/chat\",\n\thistory: \"/api/v1/msg/\",\n\tuser: \"/api/v1/user\",\n};\nexport const contentTypes = {\n\tjson: {\n\t\t\"Content-Type\": \"application/json; charset=utf-8\",\n\t},\n};\n","import React, {\n ReactElement,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { MessageDisplay } from \"../MessageDisplay/MessageDisplay\";\nimport { Client } from \"@stomp/stompjs\";\nimport { Message, MessageType } from \"../type/messageTypes\";\nimport \"./Chat.css\";\nimport strings from \"../Intl/strings.json\";\nimport { LangContext } from \"../context\";\nimport { connectionAddress, endpoints } from \"../consts\";\nconst Chat = ({ user }: { user: string }): React.ReactElement => {\n const lang = useContext(LangContext);\n const chatPage = strings[lang].chat;\n const [messages, setMessages] = useState([]);\n let stompClientRef = useRef(\n new Client({\n brokerURL: connectionAddress,\n })\n );\n // TODO solve issue with non-static markup\n stompClientRef.current.onConnect = (frame) => {\n stompClientRef.current.subscribe(\n endpoints.subscription,\n (message) => {\n console.log(\n `Collected new message: ${message.body}`\n );\n const messageBody = JSON.parse(\n message.body\n ) as Message;\n console.log(messageBody);\n setMessages((message) => {\n return message.concat([\n ,\n ]);\n });\n }\n );\n stompClientRef.current.publish({\n body: JSON.stringify({\n type: MessageType.HELLO,\n fromUserId: user,\n toUserId: \"everyone\",\n content: `${user} has joined the server!`,\n timeMillis: Date.now(),\n }),\n destination: endpoints.destination,\n });\n };\n // Generic error handlers\n stompClientRef.current.onWebSocketError = (error) => {\n console.error(\"Error with websocket\", error);\n };\n\n stompClientRef.current.onStompError = (frame) => {\n console.error(\n \"Broker reported error: \" + frame.headers[\"message\"]\n );\n console.error(\"Additional details: \" + frame.body);\n };\n\n // Button press event handler.\n const sendData = () => {\n const entryElement: HTMLInputElement = document.getElementById(\n \"data-entry\"\n ) as HTMLInputElement;\n if (entryElement.value === \"\") {\n return;\n }\n const messageData: Message = {\n type: MessageType.MESSAGE,\n fromUserId: user,\n toUserId: \"everyone\",\n content: entryElement.value,\n timeMillis: Date.now(),\n };\n console.log(\n `STOMP connection status: ${stompClientRef.current.connected}`\n );\n stompClientRef.current.publish({\n body: JSON.stringify(messageData),\n destination: endpoints.destination,\n headers: {\n \"Content-Type\":\n \"application/json; charset=utf-8\",\n },\n });\n entryElement.value = \"\";\n };\n useEffect(() => {\n // Stomp client is disconnected after each re-render\n // This should be actively avoided\n stompClientRef.current.activate();\n return () => {\n stompClientRef.current.deactivate();\n };\n }, []);\n // https://www.w3schools.com/jsref/obj_keyboardevent.asp\n document.addEventListener(\"keydown\", (ev: KeyboardEvent) => {\n if (ev.key === \"Enter\") {\n sendData();\n }\n });\n useEffect(() => {\n try {\n const elem = document.querySelector(\n \".chat-inner-wrapper\"\n );\n if (elem) {\n elem.scrollTop = elem.scrollHeight;\n } else {\n }\n } catch (err) {\n console.log(\"error encountered\");\n }\n return () => {};\n }, [messages]);\n return (\n \n \n {chatPage.window.title.replaceAll(\n \"$userName\",\n user\n )}\n \n {messages}
\n \n \n sendData()}>\n {chatPage.sendButtonPrompt}\n \n \n \n );\n};\nexport default Chat;\n","import { useContext, useState } from \"react\";\nimport { contentTypes, domain, endpoints, port } from \"../consts\";\nimport { LangContext, LoginType } from \"../context\";\nimport { User } from \"../type/userTypes\";\nimport \"./Login.css\";\nimport strings from \"../Intl/strings.json\";\nconst encrypt = (rawPasswordString: string) => {\n // TODO Encryption method stub\n return rawPasswordString;\n};\nexport const Login = ({\n setLogin,\n}: {\n setLogin: (newLogin: LoginType | undefined) => void;\n}): React.ReactElement => {\n const [valid, setValid] = useState(undefined);\n const [validText, setValidText] = useState();\n const lang = useContext(LangContext);\n const loginPage = strings[lang].login;\n const registrationHandler = () => {\n const uname = (\n document.getElementById(\"username\") as HTMLInputElement\n ).value;\n const passwd = encrypt(\n (document.getElementById(\"passwd\") as HTMLInputElement)\n .value\n );\n fetch(`http://${domain}:${port}${endpoints.user}`, {\n method: \"POST\",\n mode: \"cors\",\n headers: contentTypes.json,\n body: JSON.stringify({\n userName: uname,\n dateJoined: Date.now(),\n passwordHash: passwd,\n }),\n }).then((response) => {\n if (response.status === 400) {\n // 400 Bad request\n console.log(\"Username is taken or invalid!\");\n setValid(false);\n setValidText(\n loginPage.error.unameTakenOrInvalid\n );\n } else if (response.status === 200) {\n // 200 OK\n const futureDate = new Date();\n futureDate.setHours(futureDate.getHours() + 2);\n setLogin({\n username: uname,\n lastSeen: Date.now(),\n validUntil: futureDate.getUTCMilliseconds(),\n });\n document.title = `IRC User ${uname}`;\n }\n });\n };\n // login button press handler\n const loginHandler = () => {\n const uname = (\n document.getElementById(\"username\") as HTMLInputElement\n ).value;\n const passwd = encrypt(\n (document.getElementById(\"passwd\") as HTMLInputElement)\n .value\n );\n // async invocation of Fetch API\n fetch(\n `http://${domain}:${port}${endpoints.user}?name=${uname}`,\n {\n method: \"GET\",\n mode: \"cors\",\n }\n )\n .then((res) => {\n if (res.status === 404) {\n console.log(\n \"404 not found encountered\"\n );\n throw new Error(\n loginPage.error.unameNotExists\n );\n } else if (res.status === 200) {\n console.log(\"200 OK\");\n }\n return res.json();\n })\n .then((userObject) => {\n if (!userObject) {\n return;\n }\n const user = userObject as User;\n const validLogin = passwd === user.passwordHash;\n if (!validLogin) {\n // login invalid\n throw new Error(\n loginPage.error.passwdInvalid\n );\n } else {\n // login valid\n setValid(true);\n const validUntilDate: Date = new Date();\n validUntilDate.setHours(\n validUntilDate.getHours() + 2\n );\n setLogin({\n username: user.userName,\n lastSeen: user.lastSeen,\n validUntil: validUntilDate.getUTCMilliseconds(),\n });\n document.title = `IRC User ${uname}`;\n }\n })\n .catch((reason: Error) => {\n setValid(false);\n setValidText(reason.message);\n });\n };\n return (\n \n );\n};\n","import \"./Avatar.css\";\nimport placeholderImage from \"./placeholder.jpg\";\nexport const Avatar = () => {\n\treturn (\n\t\t\n\t\t\t
\n\t\t
\n\t);\n};\n","import \"../Sidebar.css\";\nimport { Avatar } from \"./Avatar\";\nexport const SidebarMenuItem = ({\n\ttext,\n\thref,\n\thandler,\n}: {\n\ttext: string;\n\thref?: string;\n\thandler?: () => void;\n}) => {\n\treturn (\n\t\t {\n\t\t\t\t\t\t\thandler();\n\t\t\t\t\t }\n\t\t\t\t\t: () => {}\n\t\t\t}\n\t\t>\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{href ? {text} : text} \n\t\t\t\t \n\t\t\t \n\t\t\t
\n\t\t
\n\t);\n};\nexport const SidebarMenu = ({\n\texitHandler,\n}: {\n\texitHandler: (enabled: boolean) => void;\n}) => {\n\treturn (\n\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t
exitHandler(false)}\n\t\t\t> \n\t\t
\n\t);\n};\n","import { useState } from \"react\";\nimport \"./Sidebar.css\";\nimport { SidebarMenu } from \"./Components/SidebarMenu\";\nexport const Sidebar = ({\n\tisEnabled,\n\tsetEnable,\n}: {\n\tisEnabled: boolean;\n\tsetEnable: (enabled: boolean) => void;\n}) => {\n\treturn isEnabled ? (\n\t\t\n\t\t\t
\n\t\t\t\t {\n\t\t\t\t\t\tsetEnable(value);\n\t\t\t\t\t}}\n\t\t\t\t> \n\t\t\t
\n\t\t
\n\t) : (\n\t\t<>>\n\t);\n};\n","import \"./Topbar.css\";\nimport strings from \"../Intl/strings.json\";\nimport { useContext } from \"react\";\nimport { LangContext } from \"../context\";\nimport menu from \"./menu.png\";\nexport const Topbar = ({\n\tsetSidebarEnable,\n}: {\n\tsetSidebarEnable: (enabled: boolean) => void;\n}) => {\n\tconst lang = useContext(LangContext);\n\treturn (\n\t\t\n\t\t\t
{\n\t\t\t\t\tsetSidebarEnable(true);\n\t\t\t\t}}\n\t\t\t\tsrc={menu}\n\t\t\t\twidth=\"100px\"\n\t\t\t\theight=\"100px\"\n\t\t\t\talt=\"Open Selection Menu\"\n\t\t\t>\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{strings[lang].homepage.title}\n\t\t\t\t \n\t\t\t\t{/* \n\t\t\t\t\t{strings[lang].homepage.description}\n\t\t\t\t
*/}\n\t\t\t \n\t\t
\n\t);\n};\n","import React, { useContext, useEffect, useState } from \"react\";\nimport Chat from \"./Chat/Chat\";\nimport \"./App.css\";\nimport { LangType, Message } from \"./type/messageTypes\";\nimport { MessageDisplay } from \"./MessageDisplay/MessageDisplay\";\nimport strings from \"./Intl/strings.json\";\nimport { LangContext, LoginContext, LoginType } from \"./context\";\nimport { contentTypes, domain, endpoints, port } from \"./consts\";\nimport { Login } from \"./Login/Login\";\nimport { Sidebar } from \"./Sidebar/Sidebar\";\nimport { Topbar } from \"./Topbar/Topbar\";\n// what we \"in the business\" call type gymnastics\nconst Wrapper = (): React.ReactElement => {\n\tconst [lang, setLang] = useState(\"en_US\");\n\tconst [login, setLogin] = useState(undefined);\n\tuseEffect(() => {\n\t\tdocument.title = login\n\t\t\t? `IRC logged in as ${login.username}`\n\t\t\t: \"IRC Chat\";\n\t}, [login]);\n\tconst [sidebarEnabled, setSidebarEnabled] = useState(false);\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\tsetSidebarEnabled(enabled)\n\t\t\t\t\t}\n\t\t\t\t> \n\t\t\t\t setSidebarEnabled(enabled)}\n\t\t\t\t> \n\t\t\t\t{/* callbacks for altering the Lang/Login contexts */}\n\t\t\t\t {\n\t\t\t\t\t\tsetLogin(value);\n\t\t\t\t\t}}\n\t\t\t\t> \n\t\t\t\t{login ? (\n\t\t\t\t\t {\n\t\t\t\t\t\t\tsetLang(value as LangType);\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<>>\n\t\t\t\t)}\n\t\t\t \n\t\t \n\t);\n};\nconst setNameOnServer = async (name: string) => {\n\tconst responseRaw = await fetch(\n\t\t`http://${domain}:${port}${endpoints.user}`,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tmode: \"cors\",\n\t\t\theaders: contentTypes.json,\n\t\t\tbody: JSON.stringify({\n\t\t\t\tuserName: name,\n\t\t\t\tdateJoined: Date.now(),\n\t\t\t}),\n\t\t}\n\t);\n\tif (responseRaw.status === 400) {\n\t\treturn { success: false, reason: \"Username taken or invalid!\" };\n\t} else return { success: true, reason: \"\" };\n};\nconst validateName = (name: string): boolean => {\n\t// TODO Name validation\n\treturn !(name === null || name === undefined || name === \"\");\n};\n\nconst App = ({\n\tchangeLang,\n}: {\n\tchangeLang: (value: string) => void;\n}): React.ReactElement => {\n\tconst [messages, setMessages] = useState([]);\n\tconst login = useContext(LoginContext);\n\tconst lang = useContext(LangContext);\n\tconst home = strings[lang].homepage;\n\tif (!login) {\n\t\treturn <>>;\n\t} else\n\t\treturn (\n\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tconst selection = prompt(home.newLangPrompt);\n\t\t\t\t\t\tchangeLang(selection ? (selection as LangType) : lang);\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{home.switchLang}\n\t\t\t\t \n\t\t\t\t {\n\t\t\t\t\t\t// For passing new username to the backend\n\t\t\t\t\t\t// In the future, this could be done with the async/await JS/TS syntax\n\t\t\t\t\t\tconst newUsername = prompt(\"New username: \");\n\t\t\t\t\t\tfetch(`${endpoints.user}?name=${newUsername}`, {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((response) => {\n\t\t\t\t\t\t\t\treturn response.json();\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((responseBody: { success: boolean }) => {\n\t\t\t\t\t\t\t\tif (responseBody.success) {\n\t\t\t\t\t\t\t\t\t// TODO Put new username response true handler method stub\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t\t\"Server POST message failed.\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\talert(\n\t\t\t\t\t\t\t\t\t\t\"The server encountered an internal error.\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\tChange Username\n\t\t\t\t \n\t\t\t\t{messages.map((message) => {\n\t\t\t\t\treturn ;\n\t\t\t\t})}\n\t\t\t\t{ }\n\t\t\t
\n\t\t);\n};\nexport default Wrapper;\n","import React, { createContext } from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport \"./index.css\";\nimport App from \"./App\";\nimport Wrapper from \"./App\";\nconst LangContext = createContext(\"en_US\");\nconst root = ReactDOM.createRoot(\n\tdocument.getElementById(\"root\") as HTMLElement\n);\nroot.render( );\n"],"names":["aa","require","ca","p","a","b","c","arguments","length","encodeURIComponent","da","Set","ea","fa","ha","add","ia","window","document","createElement","ja","Object","prototype","hasOwnProperty","ka","la","ma","v","d","e","f","g","this","acceptsBooleans","attributeName","attributeNamespace","mustUseProperty","propertyName","type","sanitizeURL","removeEmptyString","z","split","forEach","toLowerCase","ra","sa","toUpperCase","ta","slice","pa","isNaN","qa","call","test","oa","removeAttribute","setAttribute","setAttributeNS","replace","xlinkHref","ua","__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","va","Symbol","for","wa","ya","za","Aa","Ba","Ca","Da","Ea","Fa","Ga","Ha","Ia","Ja","iterator","Ka","La","A","assign","Ma","Error","stack","trim","match","Na","Oa","prepareStackTrace","defineProperty","set","Reflect","construct","l","h","k","displayName","includes","name","Pa","tag","render","Qa","$$typeof","_context","_payload","_init","Ra","Sa","Ta","nodeName","Va","_valueTracker","getOwnPropertyDescriptor","constructor","get","configurable","enumerable","getValue","setValue","stopTracking","Ua","Wa","checked","value","Xa","activeElement","body","Ya","defaultChecked","defaultValue","_wrapperState","initialChecked","Za","initialValue","controlled","ab","bb","cb","db","ownerDocument","eb","Array","isArray","fb","options","selected","defaultSelected","disabled","gb","dangerouslySetInnerHTML","children","hb","ib","jb","textContent","kb","lb","mb","nb","namespaceURI","innerHTML","valueOf","toString","firstChild","removeChild","appendChild","MSApp","execUnsafeLocalFunction","ob","lastChild","nodeType","nodeValue","pb","animationIterationCount","aspectRatio","borderImageOutset","borderImageSlice","borderImageWidth","boxFlex","boxFlexGroup","boxOrdinalGroup","columnCount","columns","flex","flexGrow","flexPositive","flexShrink","flexNegative","flexOrder","gridArea","gridRow","gridRowEnd","gridRowSpan","gridRowStart","gridColumn","gridColumnEnd","gridColumnSpan","gridColumnStart","fontWeight","lineClamp","lineHeight","opacity","order","orphans","tabSize","widows","zIndex","zoom","fillOpacity","floodOpacity","stopOpacity","strokeDasharray","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth","qb","rb","sb","style","indexOf","setProperty","keys","charAt","substring","tb","menuitem","area","base","br","col","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","ub","vb","is","wb","xb","target","srcElement","correspondingUseElement","parentNode","yb","zb","Ab","Bb","Cb","stateNode","Db","Eb","push","Fb","Gb","Hb","Ib","Jb","Kb","Lb","Mb","addEventListener","removeEventListener","Nb","apply","m","onError","Ob","Pb","Qb","Rb","Sb","Tb","Vb","alternate","return","flags","Wb","memoizedState","dehydrated","Xb","Zb","child","sibling","current","Yb","$b","ac","unstable_scheduleCallback","bc","unstable_cancelCallback","cc","unstable_shouldYield","dc","unstable_requestPaint","B","unstable_now","ec","unstable_getCurrentPriorityLevel","fc","unstable_ImmediatePriority","gc","unstable_UserBlockingPriority","hc","unstable_NormalPriority","ic","unstable_LowPriority","jc","unstable_IdlePriority","kc","lc","oc","Math","clz32","pc","qc","log","LN2","rc","sc","tc","uc","pendingLanes","suspendedLanes","pingedLanes","entangledLanes","entanglements","vc","xc","yc","zc","Ac","eventTimes","Cc","C","Dc","Ec","Fc","Gc","Hc","Ic","Jc","Kc","Lc","Mc","Nc","Oc","Map","Pc","Qc","Rc","Sc","delete","pointerId","Tc","nativeEvent","blockedOn","domEventName","eventSystemFlags","targetContainers","Vc","Wc","priority","isDehydrated","containerInfo","Xc","Yc","dispatchEvent","shift","Zc","$c","ad","bd","cd","ReactCurrentBatchConfig","dd","ed","transition","fd","gd","hd","id","Uc","stopPropagation","jd","kd","ld","md","nd","od","keyCode","charCode","pd","qd","rd","_reactName","_targetInst","currentTarget","isDefaultPrevented","defaultPrevented","returnValue","isPropagationStopped","preventDefault","cancelBubble","persist","isPersistent","wd","xd","yd","sd","eventPhase","bubbles","cancelable","timeStamp","Date","now","isTrusted","td","ud","view","detail","vd","Ad","screenX","screenY","clientX","clientY","pageX","pageY","ctrlKey","shiftKey","altKey","metaKey","getModifierState","zd","button","buttons","relatedTarget","fromElement","toElement","movementX","movementY","Bd","Dd","dataTransfer","Fd","Hd","animationName","elapsedTime","pseudoElement","Id","clipboardData","Jd","Ld","data","Md","Esc","Spacebar","Left","Up","Right","Down","Del","Win","Menu","Apps","Scroll","MozPrintableKey","Nd","Od","Alt","Control","Meta","Shift","Pd","Qd","key","String","fromCharCode","code","location","repeat","locale","which","Rd","Td","width","height","pressure","tangentialPressure","tiltX","tiltY","twist","pointerType","isPrimary","Vd","touches","targetTouches","changedTouches","Xd","Yd","deltaX","wheelDeltaX","deltaY","wheelDeltaY","wheelDelta","deltaZ","deltaMode","Zd","$d","ae","be","documentMode","ce","de","ee","fe","ge","he","ie","le","color","date","datetime","email","month","number","password","range","search","tel","text","time","url","week","me","ne","oe","event","listeners","pe","qe","re","se","te","ue","ve","we","xe","ye","ze","oninput","Ae","detachEvent","Be","Ce","attachEvent","De","Ee","Fe","He","Ie","Je","Ke","node","offset","nextSibling","Le","contains","compareDocumentPosition","Me","HTMLIFrameElement","contentWindow","href","Ne","contentEditable","Oe","focusedElem","selectionRange","documentElement","start","end","selectionStart","selectionEnd","min","defaultView","getSelection","extend","rangeCount","anchorNode","anchorOffset","focusNode","focusOffset","createRange","setStart","removeAllRanges","addRange","setEnd","element","left","scrollLeft","top","scrollTop","focus","Pe","Qe","Re","Se","Te","Ue","Ve","We","animationend","animationiteration","animationstart","transitionend","Xe","Ye","Ze","animation","$e","af","bf","cf","df","ef","ff","gf","hf","lf","mf","concat","nf","Ub","instance","listener","D","of","has","pf","qf","rf","random","sf","bind","capture","passive","n","t","J","x","u","w","F","tf","uf","parentWindow","vf","wf","na","xa","$a","ba","je","char","ke","unshift","xf","yf","zf","Af","Bf","Cf","Df","Ef","__html","Ff","setTimeout","Gf","clearTimeout","Hf","Promise","Jf","queueMicrotask","resolve","then","catch","If","Kf","Lf","Mf","previousSibling","Nf","Of","Pf","Qf","Rf","Sf","Tf","Uf","E","G","Vf","H","Wf","Xf","Yf","contextTypes","__reactInternalMemoizedUnmaskedChildContext","__reactInternalMemoizedMaskedChildContext","Zf","childContextTypes","$f","ag","bg","getChildContext","cg","__reactInternalMemoizedMergedChildContext","dg","eg","fg","gg","hg","jg","kg","lg","mg","ng","og","pg","qg","rg","sg","tg","ug","vg","wg","xg","yg","I","zg","Ag","Bg","elementType","deletions","Cg","pendingProps","overflow","treeContext","retryLane","Dg","mode","Eg","Fg","Gg","memoizedProps","Hg","Ig","Jg","Kg","Lg","defaultProps","Mg","Ng","Og","Pg","Qg","Rg","_currentValue","Sg","childLanes","Tg","dependencies","firstContext","lanes","Ug","Vg","context","memoizedValue","next","Wg","Xg","Yg","interleaved","Zg","$g","ah","updateQueue","baseState","firstBaseUpdate","lastBaseUpdate","shared","pending","effects","bh","ch","eventTime","lane","payload","callback","dh","K","eh","fh","gh","q","r","y","hh","ih","jh","Component","refs","kh","nh","isMounted","_reactInternals","enqueueSetState","L","lh","mh","enqueueReplaceState","enqueueForceUpdate","oh","shouldComponentUpdate","isPureReactComponent","ph","contextType","state","updater","qh","componentWillReceiveProps","UNSAFE_componentWillReceiveProps","rh","props","getDerivedStateFromProps","getSnapshotBeforeUpdate","UNSAFE_componentWillMount","componentWillMount","componentDidMount","sh","ref","_owner","_stringRef","th","join","uh","vh","index","wh","xh","yh","implementation","zh","Ah","done","Bh","Ch","Dh","Eh","Fh","Gh","Hh","Ih","tagName","Jh","Kh","Lh","M","Mh","revealOrder","Nh","Oh","_workInProgressVersionPrimary","Ph","ReactCurrentDispatcher","Qh","Rh","N","O","P","Sh","Th","Uh","Vh","Q","Wh","Xh","Yh","Zh","$h","ai","bi","ci","baseQueue","queue","di","ei","fi","lastRenderedReducer","action","hasEagerState","eagerState","lastRenderedState","dispatch","gi","hi","ii","ji","ki","getSnapshot","li","mi","R","ni","lastEffect","stores","oi","pi","qi","ri","create","destroy","deps","si","ti","ui","vi","wi","xi","yi","zi","Ai","Bi","Ci","Di","Ei","Fi","Gi","Hi","Ii","Ji","readContext","useCallback","useContext","useEffect","useImperativeHandle","useInsertionEffect","useLayoutEffect","useMemo","useReducer","useRef","useState","useDebugValue","useDeferredValue","useTransition","useMutableSource","useSyncExternalStore","useId","unstable_isNewReconciler","identifierPrefix","Ki","message","digest","Li","Mi","console","error","Ni","WeakMap","Oi","Pi","Qi","Ri","getDerivedStateFromError","componentDidCatch","Si","componentStack","Ti","pingCache","Ui","Vi","Wi","Xi","ReactCurrentOwner","Yi","Zi","$i","aj","bj","compare","cj","dj","ej","baseLanes","cachePool","transitions","fj","gj","hj","ij","jj","UNSAFE_componentWillUpdate","componentWillUpdate","componentDidUpdate","kj","lj","pendingContext","mj","Aj","Bj","Cj","Dj","nj","oj","pj","fallback","qj","rj","tj","dataset","dgst","uj","vj","_reactRetry","sj","subtreeFlags","wj","xj","isBackwards","rendering","renderingStartTime","last","tail","tailMode","yj","Ej","S","Fj","Gj","wasMultiple","multiple","suppressHydrationWarning","onClick","onclick","size","createElementNS","autoFocus","createTextNode","T","Hj","Ij","Jj","Kj","U","Lj","WeakSet","V","Mj","W","Nj","Oj","Qj","Rj","Sj","Tj","Uj","Vj","Wj","insertBefore","_reactRootContainer","Xj","X","Yj","Zj","ak","onCommitFiberUnmount","componentWillUnmount","bk","ck","dk","ek","fk","isHidden","gk","hk","display","ik","jk","kk","lk","__reactInternalSnapshotBeforeUpdate","src","Wk","mk","ceil","nk","ok","pk","Y","Z","qk","rk","sk","tk","uk","Infinity","vk","wk","xk","yk","zk","Ak","Bk","Ck","Dk","Ek","callbackNode","expirationTimes","expiredLanes","wc","callbackPriority","ig","Fk","Gk","Hk","Ik","Jk","Kk","Lk","Mk","Nk","Ok","Pk","finishedWork","finishedLanes","Qk","timeoutHandle","Rk","Sk","Tk","Uk","Vk","mutableReadLanes","Bc","Pj","onCommitFiberRoot","mc","onRecoverableError","Xk","onPostCommitFiberRoot","Yk","Zk","al","isReactComponent","pendingChildren","bl","mutableSourceEagerHydrationData","cl","cache","pendingSuspenseBoundaries","el","fl","gl","hl","il","jl","zj","$k","ll","reportError","ml","_internalRoot","nl","ol","pl","ql","sl","rl","unmount","unstable_scheduleHydration","splice","querySelectorAll","JSON","stringify","form","tl","usingClientEntryPoint","Events","ul","findFiberByHostInstance","bundleType","version","rendererPackageName","vl","rendererConfig","overrideHookState","overrideHookStateDeletePath","overrideHookStateRenamePath","overrideProps","overridePropsDeletePath","overridePropsRenamePath","setErrorHandler","setSuspenseHandler","scheduleUpdate","currentDispatcherRef","findHostInstanceByFiber","findHostInstancesForRefresh","scheduleRefresh","scheduleRoot","setRefreshHandler","getCurrentFiber","reconcilerVersion","__REACT_DEVTOOLS_GLOBAL_HOOK__","wl","isDisabled","supportsFiber","inject","exports","createPortal","dl","createRoot","unstable_strictMode","findDOMNode","flushSync","hydrate","hydrateRoot","hydratedSources","_getVersion","_source","unmountComponentAtNode","unstable_batchedUpdates","unstable_renderSubtreeIntoContainer","checkDCE","err","module","__self","__source","Fragment","jsx","jsxs","setState","forceUpdate","escape","_status","_result","default","Children","map","count","toArray","only","Profiler","PureComponent","StrictMode","Suspense","cloneElement","createContext","_currentValue2","_threadCount","Provider","Consumer","_defaultValue","_globalName","createFactory","createRef","forwardRef","isValidElement","lazy","memo","startTransition","unstable_act","pop","sortIndex","performance","setImmediate","startTime","expirationTime","priorityLevel","navigator","scheduling","isInputPending","MessageChannel","port2","port1","onmessage","postMessage","unstable_Profiling","unstable_continueExecution","unstable_forceFrameRate","floor","unstable_getFirstCallbackNode","unstable_next","unstable_pauseExecution","unstable_runWithPriority","delay","unstable_wrapCallback","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","MessageType","LangContext","LoginContext","MessageDisplay","_ref","fromUserId","toUserId","content","timeMillis","dateTime","lang","msgPage","strings","chat","timeString","getHours","getMinutes","HELLO","_jsxs","className","joinMessage","MESSAGE","serverMessage","DATA","CHNAME","_jsx","_Fragment","BYTE","FrameImpl","params","command","headers","binaryBody","escapeHeaderValues","skipContentLengthHeader","_binaryBody","isBinaryBody","_body","TextDecoder","decode","TextEncoder","encode","fromRawFrame","rawFrame","str","header","reverse","hdrValueUnEscape","serializeCmdAndHeaders","serialize","cmdAndHeaders","toUnit8Array","buffer","lines","hdrValueEscape","isBodyEmpty","bodyLength","sizeOfUTF8","s","uint8CmdAndHeaders","nullTerminator","Uint8Array","uint8Frame","marshall","Parser","onFrame","onIncomingPing","_encoder","_decoder","_token","_initState","parseChunk","segment","chunk","appendMissingNULLonIncoming","chunkWithNull","i","byte","_onByte","_collectFrame","_collectCommand","_reinjectByte","_results","_consumeTokenAsUTF8","_collectHeaders","_consumeByte","_collectHeaderKey","_setupCollectBody","_headerKey","_collectHeaderValue","contentLengthHeader","filter","_bodyBytesRemaining","parseInt","_collectBodyFixedSize","_collectBodyNullTerminated","_retrievedBody","_consumeTokenAsRaw","rawResult","StompSocketState","ActivationState","Versions","versions","supportedVersions","protocolVersions","V1_0","V1_1","V1_2","StompHandler","_client","_webSocket","config","_connected","_serverFrameHandlers","CONNECTED","frame","debug","server","_connectedVersion","_escapeHeaderValues","_setupHeartbeat","onConnect","subscription","onReceive","_subscriptions","onUnhandledMessage","client","messageId","ack","nack","RECEIPT","_receiptWatchers","onUnhandledReceipt","ERROR","onStompError","_counter","_partialData","_lastServerActivityTS","stompVersions","connectHeaders","disconnectHeaders","heartbeatIncoming","heartbeatOutgoing","splitLargeFrames","maxWebSocketChunkSize","forceBinaryWSFrames","logRawCommunication","discardWebsocketOnCommFailure","onDisconnect","onWebSocketClose","onWebSocketError","onUnhandledFrame","connectedVersion","connected","parser","evt","rawChunkAsString","ArrayBuffer","onclose","closeEvent","_cleanUp","onerror","errorEvent","onopen","_transmit","serverOutgoing","serverIncoming","ttl","max","_pinger","setInterval","readyState","OPEN","send","_ponger","delta","_closeOrDiscardWebsocket","discardWebsocket","_closeWebsocket","forceDisconnect","CONNECTING","close","webSocket","terminate","msg","noOp","ts","origOnClose","getTime","reason","wasClean","rawChunk","out","dispose","receipt","watchForReceipt","clearInterval","publish","destination","hdrs","receiptId","subscribe","unsubscribe","begin","transactionId","txId","transaction","commit","abort","subscriptionId","Client","conf","connectionTimeout","reconnectDelay","INACTIVE","beforeConnect","onChangeState","_disconnectHeaders","configure","_this$_stompHandler","_stompHandler","active","ACTIVE","_changeState","activate","_activate","_connect","DEACTIVATING","deactivate","_connectionWatcher","_createWebSocket","_disposeStompHandler","_schedule_reconnect","webSocketFactory","brokerURL","WebSocket","binaryType","_reconnector","force","needToDispose","retPromise","CLOSED","origOnWebSocketClose","reject","_this$_stompHandler2","_checkConnection","TypeError","domain","hostname","port","connectionAddress","endpoints","contentTypes","json","user","chatPage","messages","setMessages","stompClientRef","messageBody","parse","sendData","entryElement","getElementById","messageData","ev","elem","querySelector","scrollHeight","title","replaceAll","sendButtonPrompt","Login","setLogin","valid","setValid","validText","setValidText","loginPage","login","htmlFor","uname","passwd","loginHandler","fetch","method","res","status","unameNotExists","userObject","passwordHash","passwdInvalid","validUntilDate","setHours","username","userName","lastSeen","validUntil","getUTCMilliseconds","registrationHandler","dateJoined","response","unameTakenOrInvalid","futureDate","register","logout","Avatar","placeholderImage","SidebarMenuItem","handler","SidebarMenu","_ref2","exitHandler","Sidebar","isEnabled","setEnable","Topbar","setSidebarEnable","menu","alt","homepage","App","changeLang","home","selection","prompt","newLangPrompt","switchLang","newUsername","responseBody","success","alert","Chat","Wrapper","setLang","sidebarEnabled","setSidebarEnabled","enabled","ReactDOM"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/main/resources/static/static/media/menu.b641b2ee66cc66c4507a.png b/src/main/resources/static/static/media/menu.b641b2ee66cc66c4507a.png
new file mode 100644
index 0000000..ae9b91a
Binary files /dev/null and b/src/main/resources/static/static/media/menu.b641b2ee66cc66c4507a.png differ
diff --git a/src/main/resources/static/static/media/placeholder.7fd2b880a5558e1b1b67.jpg b/src/main/resources/static/static/media/placeholder.7fd2b880a5558e1b1b67.jpg
new file mode 100644
index 0000000..6789d3b
Binary files /dev/null and b/src/main/resources/static/static/media/placeholder.7fd2b880a5558e1b1b67.jpg differ